cpp/gen/command_gen.cpp#

    1//
    2// cpp/gen/command_gen.cpp
    3// Generated by AMC
    4//
    5// Copyright (C) 2008-2013 AlgoEngineering LLC
    6// Copyright (C) 2013-2019 NYSE | Intercontinental Exchange
    7// Copyright (C) 2020-2023 Astra
    8// Copyright (C) 2023 AlgoRND
    9//
   10// This program is free software: you can redistribute it and/or modify
   11// it under the terms of the GNU General Public License as published by
   12// the Free Software Foundation, either version 3 of the License, or
   13// (at your option) any later version.
   14//
   15// This program is distributed in the hope that it will be useful,
   16// but WITHOUT ANY WARRANTY; without even the implied warranty of
   17// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
   18// GNU General Public License for more details.
   19//
   20// You should have received a copy of the GNU General Public License
   21// along with this program.  If not, see <https://www.gnu.org/licenses/>.
   22//
   23
   24
   25#include "include/algo.h"  // hard-coded include
   26#include "include/gen/command_gen.h"
   27#include "include/gen/command_gen.inl.h"
   28#include "include/gen/algo_lib_gen.h"
   29#include "include/gen/algo_lib_gen.inl.h"
   30#include "include/gen/algo_gen.h"
   31#include "include/gen/algo_gen.inl.h"
   32#include "include/gen/ietf_gen.h"
   33#include "include/gen/ietf_gen.inl.h"
   34//#pragma endinclude
   35namespace command { // gen:ns_print_proto
   36    // func:command...SizeCheck
   37    static void          SizeCheck();
   38} // gen:ns_print_proto
   39
   40// --- command.FieldId.value.ToCstr
   41// Convert numeric value of field to one of predefined string constants.
   42// If string is found, return a static C string. Otherwise, return NULL.
   43const char* command::value_ToCstr(const command::FieldId& parent) {
   44    const char *ret = NULL;
   45    switch(value_GetEnum(parent)) {
   46        case command_FieldId_target        : ret = "target";  break;
   47        case command_FieldId_in            : ret = "in";  break;
   48        case command_FieldId_out_dir       : ret = "out_dir";  break;
   49        case command_FieldId_cfg           : ret = "cfg";  break;
   50        case command_FieldId_compiler      : ret = "compiler";  break;
   51        case command_FieldId_uname         : ret = "uname";  break;
   52        case command_FieldId_arch          : ret = "arch";  break;
   53        case command_FieldId_ood           : ret = "ood";  break;
   54        case command_FieldId_list          : ret = "list";  break;
   55        case command_FieldId_listincl      : ret = "listincl";  break;
   56        case command_FieldId_build         : ret = "build";  break;
   57        case command_FieldId_preproc       : ret = "preproc";  break;
   58        case command_FieldId_clean         : ret = "clean";  break;
   59        case command_FieldId_dry_run       : ret = "dry_run";  break;
   60        case command_FieldId_maxjobs       : ret = "maxjobs";  break;
   61        case command_FieldId_printcmd      : ret = "printcmd";  break;
   62        case command_FieldId_force         : ret = "force";  break;
   63        case command_FieldId_install       : ret = "install";  break;
   64        case command_FieldId_coverity      : ret = "coverity";  break;
   65        case command_FieldId_package       : ret = "package";  break;
   66        case command_FieldId_maxerr        : ret = "maxerr";  break;
   67        case command_FieldId_disas         : ret = "disas";  break;
   68        case command_FieldId_report        : ret = "report";  break;
   69        case command_FieldId_jcdb          : ret = "jcdb";  break;
   70        case command_FieldId_cache         : ret = "cache";  break;
   71        case command_FieldId_readme        : ret = "readme";  break;
   72        case command_FieldId_ns            : ret = "ns";  break;
   73        case command_FieldId_section       : ret = "section";  break;
   74        case command_FieldId_update        : ret = "update";  break;
   75        case command_FieldId_check         : ret = "check";  break;
   76        case command_FieldId_link          : ret = "link";  break;
   77        case command_FieldId_anchor        : ret = "anchor";  break;
   78        case command_FieldId_print         : ret = "print";  break;
   79        case command_FieldId_query         : ret = "query";  break;
   80        case command_FieldId_where         : ret = "where";  break;
   81        case command_FieldId_del           : ret = "del";  break;
   82        case command_FieldId_sel           : ret = "sel";  break;
   83        case command_FieldId_insert        : ret = "insert";  break;
   84        case command_FieldId_replace       : ret = "replace";  break;
   85        case command_FieldId_merge         : ret = "merge";  break;
   86        case command_FieldId_unused        : ret = "unused";  break;
   87        case command_FieldId_trunc         : ret = "trunc";  break;
   88        case command_FieldId_selerr        : ret = "selerr";  break;
   89        case command_FieldId_maxshow       : ret = "maxshow";  break;
   90        case command_FieldId_write         : ret = "write";  break;
   91        case command_FieldId_rename        : ret = "rename";  break;
   92        case command_FieldId_nup           : ret = "nup";  break;
   93        case command_FieldId_ndown         : ret = "ndown";  break;
   94        case command_FieldId_l             : ret = "l";  break;
   95        case command_FieldId_xref          : ret = "xref";  break;
   96        case command_FieldId_fldfunc       : ret = "fldfunc";  break;
   97        case command_FieldId_maxgroup      : ret = "maxgroup";  break;
   98        case command_FieldId_pretty        : ret = "pretty";  break;
   99        case command_FieldId_tree          : ret = "tree";  break;
  100        case command_FieldId_loose         : ret = "loose";  break;
  101        case command_FieldId_my            : ret = "my";  break;
  102        case command_FieldId_schema        : ret = "schema";  break;
  103        case command_FieldId_e             : ret = "e";  break;
  104        case command_FieldId_t             : ret = "t";  break;
  105        case command_FieldId_g             : ret = "g";  break;
  106        case command_FieldId_x             : ret = "x";  break;
  107        case command_FieldId_rowid         : ret = "rowid";  break;
  108        case command_FieldId_cmt           : ret = "cmt";  break;
  109        case command_FieldId_cmd           : ret = "cmd";  break;
  110        case command_FieldId_field         : ret = "field";  break;
  111        case command_FieldId_regxof        : ret = "regxof";  break;
  112        case command_FieldId_meta          : ret = "meta";  break;
  113        case command_FieldId_data          : ret = "data";  break;
  114        case command_FieldId_line          : ret = "line";  break;
  115        case command_FieldId_point         : ret = "point";  break;
  116        case command_FieldId_type          : ret = "type";  break;
  117        case command_FieldId_debug_log     : ret = "debug_log";  break;
  118        case command_FieldId_arg           : ret = "arg";  break;
  119        case command_FieldId_write_ours    : ret = "write_ours";  break;
  120        case command_FieldId_msize         : ret = "msize";  break;
  121        case command_FieldId_create        : ret = "create";  break;
  122        case command_FieldId_finput        : ret = "finput";  break;
  123        case command_FieldId_foutput       : ret = "foutput";  break;
  124        case command_FieldId_srcfile       : ret = "srcfile";  break;
  125        case command_FieldId_gstatic       : ret = "gstatic";  break;
  126        case command_FieldId_indexed       : ret = "indexed";  break;
  127        case command_FieldId_nstype        : ret = "nstype";  break;
  128        case command_FieldId_ctype         : ret = "ctype";  break;
  129        case command_FieldId_pooltype      : ret = "pooltype";  break;
  130        case command_FieldId_ssimfile      : ret = "ssimfile";  break;
  131        case command_FieldId_subset        : ret = "subset";  break;
  132        case command_FieldId_subset2       : ret = "subset2";  break;
  133        case command_FieldId_separator     : ret = "separator";  break;
  134        case command_FieldId_dflt          : ret = "dflt";  break;
  135        case command_FieldId_anon          : ret = "anon";  break;
  136        case command_FieldId_bigend        : ret = "bigend";  break;
  137        case command_FieldId_cascdel       : ret = "cascdel";  break;
  138        case command_FieldId_before        : ret = "before";  break;
  139        case command_FieldId_substr        : ret = "substr";  break;
  140        case command_FieldId_alias         : ret = "alias";  break;
  141        case command_FieldId_srcfield      : ret = "srcfield";  break;
  142        case command_FieldId_fstep         : ret = "fstep";  break;
  143        case command_FieldId_inscond       : ret = "inscond";  break;
  144        case command_FieldId_reftype       : ret = "reftype";  break;
  145        case command_FieldId_hashfld       : ret = "hashfld";  break;
  146        case command_FieldId_sortfld       : ret = "sortfld";  break;
  147        case command_FieldId_unittest      : ret = "unittest";  break;
  148        case command_FieldId_citest        : ret = "citest";  break;
  149        case command_FieldId_cppfunc       : ret = "cppfunc";  break;
  150        case command_FieldId_via           : ret = "via";  break;
  151        case command_FieldId_comment       : ret = "comment";  break;
  152        case command_FieldId_sandbox       : ret = "sandbox";  break;
  153        case command_FieldId_test          : ret = "test";  break;
  154        case command_FieldId_showcpp       : ret = "showcpp";  break;
  155        case command_FieldId_msgtype       : ret = "msgtype";  break;
  156        case command_FieldId_anonfld       : ret = "anonfld";  break;
  157        case command_FieldId_sigcheck      : ret = "sigcheck";  break;
  158        case command_FieldId_data_dir      : ret = "data_dir";  break;
  159        case command_FieldId_related       : ret = "related";  break;
  160        case command_FieldId_notssimfile   : ret = "notssimfile";  break;
  161        case command_FieldId_checkable     : ret = "checkable";  break;
  162        case command_FieldId_r             : ret = "r";  break;
  163        case command_FieldId_nsdb          : ret = "nsdb";  break;
  164        case command_FieldId_fkey          : ret = "fkey";  break;
  165        case command_FieldId_start         : ret = "start";  break;
  166        case command_FieldId_stop          : ret = "stop";  break;
  167        case command_FieldId_abort         : ret = "abort";  break;
  168        case command_FieldId_shell         : ret = "shell";  break;
  169        case command_FieldId_serv          : ret = "serv";  break;
  170        case command_FieldId_in_dir        : ret = "in_dir";  break;
  171        case command_FieldId_proto         : ret = "proto";  break;
  172        case command_FieldId_trace         : ret = "trace";  break;
  173        case command_FieldId_key           : ret = "key";  break;
  174        case command_FieldId_include       : ret = "include";  break;
  175        case command_FieldId_dot           : ret = "dot";  break;
  176        case command_FieldId_xns           : ret = "xns";  break;
  177        case command_FieldId_noinput       : ret = "noinput";  break;
  178        case command_FieldId_render        : ret = "render";  break;
  179        case command_FieldId_id            : ret = "id";  break;
  180        case command_FieldId_file_prefix   : ret = "file_prefix";  break;
  181        case command_FieldId_nchild        : ret = "nchild";  break;
  182        case command_FieldId_blocking      : ret = "blocking";  break;
  183        case command_FieldId_nmsg          : ret = "nmsg";  break;
  184        case command_FieldId_timeout       : ret = "timeout";  break;
  185        case command_FieldId_recvdelay_ns  : ret = "recvdelay_ns";  break;
  186        case command_FieldId_senddelay_ns  : ret = "senddelay_ns";  break;
  187        case command_FieldId_msgsize_min   : ret = "msgsize_min";  break;
  188        case command_FieldId_msgsize_max   : ret = "msgsize_max";  break;
  189        case command_FieldId_bufsize       : ret = "bufsize";  break;
  190        case command_FieldId_recvdelay     : ret = "recvdelay";  break;
  191        case command_FieldId_pkgdata       : ret = "pkgdata";  break;
  192        case command_FieldId_diff          : ret = "diff";  break;
  193        case command_FieldId_push          : ret = "push";  break;
  194        case command_FieldId_remove        : ret = "remove";  break;
  195        case command_FieldId_origin        : ret = "origin";  break;
  196        case command_FieldId_ref           : ret = "ref";  break;
  197        case command_FieldId_showrec       : ret = "showrec";  break;
  198        case command_FieldId_showfile      : ret = "showfile";  break;
  199        case command_FieldId_R             : ret = "R";  break;
  200        case command_FieldId_reset         : ret = "reset";  break;
  201        case command_FieldId_checkclean    : ret = "checkclean";  break;
  202        case command_FieldId_stat          : ret = "stat";  break;
  203        case command_FieldId_annotate      : ret = "annotate";  break;
  204        case command_FieldId_data_in       : ret = "data_in";  break;
  205        case command_FieldId_binpath       : ret = "binpath";  break;
  206        case command_FieldId_amctest       : ret = "amctest";  break;
  207        case command_FieldId_dofork        : ret = "dofork";  break;
  208        case command_FieldId_q             : ret = "q";  break;
  209        case command_FieldId_cijob         : ret = "cijob";  break;
  210        case command_FieldId_capture       : ret = "capture";  break;
  211        case command_FieldId_exec          : ret = "exec";  break;
  212        case command_FieldId_astr          : ret = "astr";  break;
  213        case command_FieldId_anum          : ret = "anum";  break;
  214        case command_FieldId_adbl          : ret = "adbl";  break;
  215        case command_FieldId_aflag         : ret = "aflag";  break;
  216        case command_FieldId_str           : ret = "str";  break;
  217        case command_FieldId_num           : ret = "num";  break;
  218        case command_FieldId_dbl           : ret = "dbl";  break;
  219        case command_FieldId_flag          : ret = "flag";  break;
  220        case command_FieldId_dstr          : ret = "dstr";  break;
  221        case command_FieldId_dnum          : ret = "dnum";  break;
  222        case command_FieldId_ddbl          : ret = "ddbl";  break;
  223        case command_FieldId_dflag         : ret = "dflag";  break;
  224        case command_FieldId_mstr          : ret = "mstr";  break;
  225        case command_FieldId_mnum          : ret = "mnum";  break;
  226        case command_FieldId_mdbl          : ret = "mdbl";  break;
  227        case command_FieldId_amnum         : ret = "amnum";  break;
  228        case command_FieldId_fconst        : ret = "fconst";  break;
  229        case command_FieldId_cconst        : ret = "cconst";  break;
  230        case command_FieldId_dregx         : ret = "dregx";  break;
  231        case command_FieldId_dpkey         : ret = "dpkey";  break;
  232        case command_FieldId_comptest      : ret = "comptest";  break;
  233        case command_FieldId_mdbg          : ret = "mdbg";  break;
  234        case command_FieldId_run           : ret = "run";  break;
  235        case command_FieldId_printinput    : ret = "printinput";  break;
  236        case command_FieldId_normalize     : ret = "normalize";  break;
  237        case command_FieldId_covcapture    : ret = "covcapture";  break;
  238        case command_FieldId_covcheck      : ret = "covcheck";  break;
  239        case command_FieldId_compdir       : ret = "compdir";  break;
  240        case command_FieldId_check_untracked: ret = "check_untracked";  break;
  241        case command_FieldId_memcheck      : ret = "memcheck";  break;
  242        case command_FieldId_callgrind     : ret = "callgrind";  break;
  243        case command_FieldId_stream        : ret = "stream";  break;
  244        case command_FieldId_i             : ret = "i";  break;
  245        case command_FieldId_b             : ret = "b";  break;
  246        case command_FieldId_covdir        : ret = "covdir";  break;
  247        case command_FieldId_logfile       : ret = "logfile";  break;
  248        case command_FieldId_runcmd        : ret = "runcmd";  break;
  249        case command_FieldId_exclude       : ret = "exclude";  break;
  250        case command_FieldId_mergepath     : ret = "mergepath";  break;
  251        case command_FieldId_gcov          : ret = "gcov";  break;
  252        case command_FieldId_ssim          : ret = "ssim";  break;
  253        case command_FieldId_xmlpretty     : ret = "xmlpretty";  break;
  254        case command_FieldId_summary       : ret = "summary";  break;
  255        case command_FieldId_reprofile     : ret = "reprofile";  break;
  256        case command_FieldId_args          : ret = "args";  break;
  257        case command_FieldId_inputfile     : ret = "inputfile";  break;
  258        case command_FieldId_fuzzstrat     : ret = "fuzzstrat";  break;
  259        case command_FieldId_seed          : ret = "seed";  break;
  260        case command_FieldId_testprob      : ret = "testprob";  break;
  261        case command_FieldId_gtblacttst    : ret = "gtblacttst";  break;
  262        case command_FieldId_mr            : ret = "mr";  break;
  263        case command_FieldId_note          : ret = "note";  break;
  264        case command_FieldId_skip_init     : ret = "skip_init";  break;
  265        case command_FieldId_skip_git_init : ret = "skip_git_init";  break;
  266        case command_FieldId_randomize_ports: ret = "randomize_ports";  break;
  267        case command_FieldId_ncmd          : ret = "ncmd";  break;
  268        case command_FieldId_nofork        : ret = "nofork";  break;
  269        case command_FieldId_perf_secs     : ret = "perf_secs";  break;
  270        case command_FieldId_pertest_timeout: ret = "pertest_timeout";  break;
  271        case command_FieldId_wtblacttst    : ret = "wtblacttst";  break;
  272        case command_FieldId_wiki_envnode  : ret = "wiki_envnode";  break;
  273        case command_FieldId_wiki_kill     : ret = "wiki_kill";  break;
  274        case command_FieldId_wiki_start    : ret = "wiki_start";  break;
  275        case command_FieldId_wiki_update   : ret = "wiki_update";  break;
  276        case command_FieldId_wiki_update_help: ret = "wiki_update_help";  break;
  277        case command_FieldId_wiki_update_fields: ret = "wiki_update_fields";  break;
  278        case command_FieldId_wiki_clean_start: ret = "wiki_clean_start";  break;
  279        case command_FieldId_authdir_dflt  : ret = "authdir_dflt";  break;
  280        case command_FieldId_wikisite      : ret = "wikisite";  break;
  281        case command_FieldId_cmd_dctr      : ret = "cmd_dctr";  break;
  282        case command_FieldId_config_ssh    : ret = "config_ssh";  break;
  283        case command_FieldId_config_ssh_vpn: ret = "config_ssh_vpn";  break;
  284        case command_FieldId_clean_run     : ret = "clean_run";  break;
  285        case command_FieldId_inspect       : ret = "inspect";  break;
  286        case command_FieldId_logs          : ret = "logs";  break;
  287        case command_FieldId_generate_build: ret = "generate_build";  break;
  288        case command_FieldId_dhost         : ret = "dhost";  break;
  289        case command_FieldId_dctr          : ret = "dctr";  break;
  290        case command_FieldId_dctrhost      : ret = "dctrhost";  break;
  291        case command_FieldId_script_dir    : ret = "script_dir";  break;
  292        case command_FieldId_cloneto       : ret = "cloneto";  break;
  293        case command_FieldId_renameto      : ret = "renameto";  break;
  294        case command_FieldId_purge         : ret = "purge";  break;
  295        case command_FieldId_deep          : ret = "deep";  break;
  296        case command_FieldId_sync          : ret = "sync";  break;
  297        case command_FieldId_keep_files    : ret = "keep_files";  break;
  298        case command_FieldId_dctr_from     : ret = "dctr_from";  break;
  299        case command_FieldId_dhost_ip      : ret = "dhost_ip";  break;
  300        case command_FieldId_dhost_ext_ip  : ret = "dhost_ext_ip";  break;
  301        case command_FieldId_dhost_ext_port: ret = "dhost_ext_port";  break;
  302        case command_FieldId_dhost_user    : ret = "dhost_user";  break;
  303        case command_FieldId_genamc        : ret = "genamc";  break;
  304        case command_FieldId_xml           : ret = "xml";  break;
  305        case command_FieldId_add_reset     : ret = "add_reset";  break;
  306        case command_FieldId_encode        : ret = "encode";  break;
  307        case command_FieldId_decode        : ret = "decode";  break;
  308        case command_FieldId_enc           : ret = "enc";  break;
  309        case command_FieldId_nullable      : ret = "nullable";  break;
  310        case command_FieldId_ifmt          : ret = "ifmt";  break;
  311        case command_FieldId_ofmt          : ret = "ofmt";  break;
  312        case command_FieldId_echo          : ret = "echo";  break;
  313        case command_FieldId_hex           : ret = "hex";  break;
  314        case command_FieldId_fix_bs        : ret = "fix_bs";  break;
  315        case command_FieldId_fix_soh       : ret = "fix_soh";  break;
  316        case command_FieldId_fix_nl        : ret = "fix_nl";  break;
  317        case command_FieldId_fast_msg_max  : ret = "fast_msg_max";  break;
  318        case command_FieldId_stats         : ret = "stats";  break;
  319        case command_FieldId_enable        : ret = "enable";  break;
  320        case command_FieldId_disable       : ret = "disable";  break;
  321        case command_FieldId_gc            : ret = "gc";  break;
  322        case command_FieldId_dir           : ret = "dir";  break;
  323        case command_FieldId_hitrate       : ret = "hitrate";  break;
  324        case command_FieldId_after         : ret = "after";  break;
  325        case command_FieldId_selector      : ret = "selector";  break;
  326        case command_FieldId_fields        : ret = "fields";  break;
  327        case command_FieldId_accept        : ret = "accept";  break;
  328        case command_FieldId_approve       : ret = "approve";  break;
  329        case command_FieldId_needs_work    : ret = "needs_work";  break;
  330        case command_FieldId_authdir       : ret = "authdir";  break;
  331        case command_FieldId_gitdir        : ret = "gitdir";  break;
  332        case command_FieldId_show_gitlab_system_notes: ret = "show_gitlab_system_notes";  break;
  333        case command_FieldId_gmvproj       : ret = "gmvproj";  break;
  334        case command_FieldId_move_out      : ret = "move_out";  break;
  335        case command_FieldId_move_in       : ret = "move_in";  break;
  336        case command_FieldId_commit        : ret = "commit";  break;
  337        case command_FieldId_no_rebase     : ret = "no_rebase";  break;
  338        case command_FieldId_attach        : ret = "attach";  break;
  339        case command_FieldId_catchthrow    : ret = "catchthrow";  break;
  340        case command_FieldId_tui           : ret = "tui";  break;
  341        case command_FieldId_bcmd          : ret = "bcmd";  break;
  342        case command_FieldId_emacs         : ret = "emacs";  break;
  343        case command_FieldId_manywin       : ret = "manywin";  break;
  344        case command_FieldId_follow_child  : ret = "follow_child";  break;
  345        case command_FieldId_py            : ret = "py";  break;
  346        case command_FieldId_writessimfile : ret = "writessimfile";  break;
  347        case command_FieldId_url           : ret = "url";  break;
  348        case command_FieldId_tables        : ret = "tables";  break;
  349        case command_FieldId_nologo        : ret = "nologo";  break;
  350        case command_FieldId_baddbok       : ret = "baddbok";  break;
  351        case command_FieldId_move          : ret = "move";  break;
  352        case command_FieldId_dedup         : ret = "dedup";  break;
  353        case command_FieldId_undo          : ret = "undo";  break;
  354        case command_FieldId_hash          : ret = "hash";  break;
  355        case command_FieldId_expr          : ret = "expr";  break;
  356        case command_FieldId_style         : ret = "style";  break;
  357        case command_FieldId_match         : ret = "match";  break;
  358        case command_FieldId_string        : ret = "string";  break;
  359        case command_FieldId_show          : ret = "show";  break;
  360        case command_FieldId_name          : ret = "name";  break;
  361        case command_FieldId_files         : ret = "files";  break;
  362        case command_FieldId_refs          : ret = "refs";  break;
  363        case command_FieldId_site          : ret = "site";  break;
  364        case command_FieldId_builder       : ret = "builder";  break;
  365        case command_FieldId_build_fail_on_warning: ret = "build_fail_on_warning";  break;
  366        case command_FieldId_build_quiet   : ret = "build_quiet";  break;
  367        case command_FieldId_jobs          : ret = "jobs";  break;
  368        case command_FieldId_preserve_footer: ret = "preserve_footer";  break;
  369        case command_FieldId_serve         : ret = "serve";  break;
  370        case command_FieldId_serve_dctrhost: ret = "serve_dctrhost";  break;
  371        case command_FieldId_serve_pages   : ret = "serve_pages";  break;
  372        case command_FieldId_serve_verify  : ret = "serve_verify";  break;
  373        case command_FieldId_workdir       : ret = "workdir";  break;
  374        case command_FieldId_opts_numbered : ret = "opts_numbered";  break;
  375        case command_FieldId_body          : ret = "body";  break;
  376        case command_FieldId_targsrc       : ret = "targsrc";  break;
  377        case command_FieldId_func          : ret = "func";  break;
  378        case command_FieldId_nextfile      : ret = "nextfile";  break;
  379        case command_FieldId_other         : ret = "other";  break;
  380        case command_FieldId_updateproto   : ret = "updateproto";  break;
  381        case command_FieldId_listfunc      : ret = "listfunc";  break;
  382        case command_FieldId_iffy          : ret = "iffy";  break;
  383        case command_FieldId_gen           : ret = "gen";  break;
  384        case command_FieldId_showloc       : ret = "showloc";  break;
  385        case command_FieldId_showstatic    : ret = "showstatic";  break;
  386        case command_FieldId_showsortkey   : ret = "showsortkey";  break;
  387        case command_FieldId_sortname      : ret = "sortname";  break;
  388        case command_FieldId_baddecl       : ret = "baddecl";  break;
  389        case command_FieldId_indent        : ret = "indent";  break;
  390        case command_FieldId_update_copyright: ret = "update_copyright";  break;
  391        case command_FieldId_scriptfile    : ret = "scriptfile";  break;
  392        case command_FieldId_linelim       : ret = "linelim";  break;
  393        case command_FieldId_strayfile     : ret = "strayfile";  break;
  394        case command_FieldId_badchar       : ret = "badchar";  break;
  395        case command_FieldId_badline       : ret = "badline";  break;
  396        case command_FieldId_expand        : ret = "expand";  break;
  397        case command_FieldId_ignoreQuote   : ret = "ignoreQuote";  break;
  398        case command_FieldId_maxpacket     : ret = "maxpacket";  break;
  399        case command_FieldId_db            : ret = "db";  break;
  400        case command_FieldId_createdb      : ret = "createdb";  break;
  401        case command_FieldId_typetag       : ret = "typetag";  break;
  402        case command_FieldId_format        : ret = "format";  break;
  403        case command_FieldId_tocamelcase   : ret = "tocamelcase";  break;
  404        case command_FieldId_tolowerunder  : ret = "tolowerunder";  break;
  405        case command_FieldId_pathcomp      : ret = "pathcomp";  break;
  406        case command_FieldId_fname         : ret = "fname";  break;
  407        case command_FieldId_outseparator  : ret = "outseparator";  break;
  408        case command_FieldId_header        : ret = "header";  break;
  409        case command_FieldId_prefer_signed : ret = "prefer_signed";  break;
  410        case command_FieldId_begin         : ret = "begin";  break;
  411        case command_FieldId_end           : ret = "end";  break;
  412        case command_FieldId_stay          : ret = "stay";  break;
  413        case command_FieldId_from          : ret = "from";  break;
  414        case command_FieldId_to            : ret = "to";  break;
  415        case command_FieldId_download      : ret = "download";  break;
  416        case command_FieldId_upload        : ret = "upload";  break;
  417        case command_FieldId_ignore_saved_edit: ret = "ignore_saved_edit";  break;
  418        case command_FieldId_exclude_subpages: ret = "exclude_subpages";  break;
  419        case command_FieldId_pagedir       : ret = "pagedir";  break;
  420        case command_FieldId_envnode       : ret = "envnode";  break;
  421        case command_FieldId_admsvc        : ret = "admsvc";  break;
  422        case command_FieldId_generate      : ret = "generate";  break;
  423        case command_FieldId_verify        : ret = "verify";  break;
  424        case command_FieldId_prefix        : ret = "prefix";  break;
  425        case command_FieldId_root          : ret = "root";  break;
  426        case command_FieldId_user          : ret = "user";  break;
  427        case command_FieldId_rsync_put     : ret = "rsync_put";  break;
  428        case command_FieldId_rsync_get     : ret = "rsync_get";  break;
  429        case command_FieldId_rsync_opts    : ret = "rsync_opts";  break;
  430        case command_FieldId_local         : ret = "local";  break;
  431        case command_FieldId_remote        : ret = "remote";  break;
  432        case command_FieldId_maxtime       : ret = "maxtime";  break;
  433        case command_FieldId_gw            : ret = "gw";  break;
  434        case command_FieldId_value         : ret = "value";  break;
  435    }
  436    return ret;
  437}
  438
  439// --- command.FieldId.value.Print
  440// Convert value to a string. First, attempt conversion to a known string.
  441// If no string matches, print value as a numeric value.
  442void command::value_Print(const command::FieldId& parent, algo::cstring &lhs) {
  443    const char *strval = value_ToCstr(parent);
  444    if (strval) {
  445        lhs << strval;
  446    } else {
  447        lhs << parent.value;
  448    }
  449}
  450
  451// --- command.FieldId.value.SetStrptrMaybe
  452// Convert string to field.
  453// If the string is invalid, do not modify field and return false.
  454// In case of success, return true
  455bool command::value_SetStrptrMaybe(command::FieldId& parent, algo::strptr rhs) {
  456    bool ret = false;
  457    switch (elems_N(rhs)) {
  458        case 1: {
  459            switch (u64(rhs[0])) {
  460                case 'R': {
  461                    value_SetEnum(parent,command_FieldId_R); ret = true; break;
  462                }
  463                case 'b': {
  464                    value_SetEnum(parent,command_FieldId_b); ret = true; break;
  465                }
  466                case 'e': {
  467                    value_SetEnum(parent,command_FieldId_e); ret = true; break;
  468                }
  469                case 'g': {
  470                    value_SetEnum(parent,command_FieldId_g); ret = true; break;
  471                }
  472                case 'i': {
  473                    value_SetEnum(parent,command_FieldId_i); ret = true; break;
  474                }
  475                case 'l': {
  476                    value_SetEnum(parent,command_FieldId_l); ret = true; break;
  477                }
  478                case 'q': {
  479                    value_SetEnum(parent,command_FieldId_q); ret = true; break;
  480                }
  481                case 'r': {
  482                    value_SetEnum(parent,command_FieldId_r); ret = true; break;
  483                }
  484                case 't': {
  485                    value_SetEnum(parent,command_FieldId_t); ret = true; break;
  486                }
  487                case 'x': {
  488                    value_SetEnum(parent,command_FieldId_x); ret = true; break;
  489                }
  490            }
  491            break;
  492        }
  493        case 2: {
  494            switch (u64(algo::ReadLE16(rhs.elems))) {
  495                case LE_STR2('d','b'): {
  496                    value_SetEnum(parent,command_FieldId_db); ret = true; break;
  497                }
  498                case LE_STR2('g','c'): {
  499                    value_SetEnum(parent,command_FieldId_gc); ret = true; break;
  500                }
  501                case LE_STR2('g','w'): {
  502                    value_SetEnum(parent,command_FieldId_gw); ret = true; break;
  503                }
  504                case LE_STR2('i','d'): {
  505                    value_SetEnum(parent,command_FieldId_id); ret = true; break;
  506                }
  507                case LE_STR2('i','n'): {
  508                    value_SetEnum(parent,command_FieldId_in); ret = true; break;
  509                }
  510                case LE_STR2('m','r'): {
  511                    value_SetEnum(parent,command_FieldId_mr); ret = true; break;
  512                }
  513                case LE_STR2('m','y'): {
  514                    value_SetEnum(parent,command_FieldId_my); ret = true; break;
  515                }
  516                case LE_STR2('n','s'): {
  517                    value_SetEnum(parent,command_FieldId_ns); ret = true; break;
  518                }
  519                case LE_STR2('p','y'): {
  520                    value_SetEnum(parent,command_FieldId_py); ret = true; break;
  521                }
  522                case LE_STR2('t','o'): {
  523                    value_SetEnum(parent,command_FieldId_to); ret = true; break;
  524                }
  525            }
  526            break;
  527        }
  528        case 3: {
  529            switch (u64(algo::ReadLE16(rhs.elems))|(u64(rhs[2])<<16)) {
  530                case LE_STR3('a','r','g'): {
  531                    value_SetEnum(parent,command_FieldId_arg); ret = true; break;
  532                }
  533                case LE_STR3('c','f','g'): {
  534                    value_SetEnum(parent,command_FieldId_cfg); ret = true; break;
  535                }
  536                case LE_STR3('c','m','d'): {
  537                    value_SetEnum(parent,command_FieldId_cmd); ret = true; break;
  538                }
  539                case LE_STR3('c','m','t'): {
  540                    value_SetEnum(parent,command_FieldId_cmt); ret = true; break;
  541                }
  542                case LE_STR3('d','b','l'): {
  543                    value_SetEnum(parent,command_FieldId_dbl); ret = true; break;
  544                }
  545                case LE_STR3('d','e','l'): {
  546                    value_SetEnum(parent,command_FieldId_del); ret = true; break;
  547                }
  548                case LE_STR3('d','i','r'): {
  549                    value_SetEnum(parent,command_FieldId_dir); ret = true; break;
  550                }
  551                case LE_STR3('d','o','t'): {
  552                    value_SetEnum(parent,command_FieldId_dot); ret = true; break;
  553                }
  554                case LE_STR3('e','n','c'): {
  555                    value_SetEnum(parent,command_FieldId_enc); ret = true; break;
  556                }
  557                case LE_STR3('e','n','d'): {
  558                    value_SetEnum(parent,command_FieldId_end); ret = true; break;
  559                }
  560                case LE_STR3('g','e','n'): {
  561                    value_SetEnum(parent,command_FieldId_gen); ret = true; break;
  562                }
  563                case LE_STR3('h','e','x'): {
  564                    value_SetEnum(parent,command_FieldId_hex); ret = true; break;
  565                }
  566                case LE_STR3('k','e','y'): {
  567                    value_SetEnum(parent,command_FieldId_key); ret = true; break;
  568                }
  569                case LE_STR3('n','u','m'): {
  570                    value_SetEnum(parent,command_FieldId_num); ret = true; break;
  571                }
  572                case LE_STR3('n','u','p'): {
  573                    value_SetEnum(parent,command_FieldId_nup); ret = true; break;
  574                }
  575                case LE_STR3('o','o','d'): {
  576                    value_SetEnum(parent,command_FieldId_ood); ret = true; break;
  577                }
  578                case LE_STR3('r','e','f'): {
  579                    value_SetEnum(parent,command_FieldId_ref); ret = true; break;
  580                }
  581                case LE_STR3('r','u','n'): {
  582                    value_SetEnum(parent,command_FieldId_run); ret = true; break;
  583                }
  584                case LE_STR3('s','e','l'): {
  585                    value_SetEnum(parent,command_FieldId_sel); ret = true; break;
  586                }
  587                case LE_STR3('s','t','r'): {
  588                    value_SetEnum(parent,command_FieldId_str); ret = true; break;
  589                }
  590                case LE_STR3('t','u','i'): {
  591                    value_SetEnum(parent,command_FieldId_tui); ret = true; break;
  592                }
  593                case LE_STR3('u','r','l'): {
  594                    value_SetEnum(parent,command_FieldId_url); ret = true; break;
  595                }
  596                case LE_STR3('v','i','a'): {
  597                    value_SetEnum(parent,command_FieldId_via); ret = true; break;
  598                }
  599                case LE_STR3('x','m','l'): {
  600                    value_SetEnum(parent,command_FieldId_xml); ret = true; break;
  601                }
  602                case LE_STR3('x','n','s'): {
  603                    value_SetEnum(parent,command_FieldId_xns); ret = true; break;
  604                }
  605            }
  606            break;
  607        }
  608        case 4: {
  609            switch (u64(algo::ReadLE32(rhs.elems))) {
  610                case LE_STR4('a','d','b','l'): {
  611                    value_SetEnum(parent,command_FieldId_adbl); ret = true; break;
  612                }
  613                case LE_STR4('a','n','o','n'): {
  614                    value_SetEnum(parent,command_FieldId_anon); ret = true; break;
  615                }
  616                case LE_STR4('a','n','u','m'): {
  617                    value_SetEnum(parent,command_FieldId_anum); ret = true; break;
  618                }
  619                case LE_STR4('a','r','c','h'): {
  620                    value_SetEnum(parent,command_FieldId_arch); ret = true; break;
  621                }
  622                case LE_STR4('a','r','g','s'): {
  623                    value_SetEnum(parent,command_FieldId_args); ret = true; break;
  624                }
  625                case LE_STR4('a','s','t','r'): {
  626                    value_SetEnum(parent,command_FieldId_astr); ret = true; break;
  627                }
  628                case LE_STR4('b','c','m','d'): {
  629                    value_SetEnum(parent,command_FieldId_bcmd); ret = true; break;
  630                }
  631                case LE_STR4('b','o','d','y'): {
  632                    value_SetEnum(parent,command_FieldId_body); ret = true; break;
  633                }
  634                case LE_STR4('d','a','t','a'): {
  635                    value_SetEnum(parent,command_FieldId_data); ret = true; break;
  636                }
  637                case LE_STR4('d','c','t','r'): {
  638                    value_SetEnum(parent,command_FieldId_dctr); ret = true; break;
  639                }
  640                case LE_STR4('d','d','b','l'): {
  641                    value_SetEnum(parent,command_FieldId_ddbl); ret = true; break;
  642                }
  643                case LE_STR4('d','e','e','p'): {
  644                    value_SetEnum(parent,command_FieldId_deep); ret = true; break;
  645                }
  646                case LE_STR4('d','f','l','t'): {
  647                    value_SetEnum(parent,command_FieldId_dflt); ret = true; break;
  648                }
  649                case LE_STR4('d','i','f','f'): {
  650                    value_SetEnum(parent,command_FieldId_diff); ret = true; break;
  651                }
  652                case LE_STR4('d','n','u','m'): {
  653                    value_SetEnum(parent,command_FieldId_dnum); ret = true; break;
  654                }
  655                case LE_STR4('d','s','t','r'): {
  656                    value_SetEnum(parent,command_FieldId_dstr); ret = true; break;
  657                }
  658                case LE_STR4('e','c','h','o'): {
  659                    value_SetEnum(parent,command_FieldId_echo); ret = true; break;
  660                }
  661                case LE_STR4('e','x','e','c'): {
  662                    value_SetEnum(parent,command_FieldId_exec); ret = true; break;
  663                }
  664                case LE_STR4('e','x','p','r'): {
  665                    value_SetEnum(parent,command_FieldId_expr); ret = true; break;
  666                }
  667                case LE_STR4('f','k','e','y'): {
  668                    value_SetEnum(parent,command_FieldId_fkey); ret = true; break;
  669                }
  670                case LE_STR4('f','l','a','g'): {
  671                    value_SetEnum(parent,command_FieldId_flag); ret = true; break;
  672                }
  673                case LE_STR4('f','r','o','m'): {
  674                    value_SetEnum(parent,command_FieldId_from); ret = true; break;
  675                }
  676                case LE_STR4('f','u','n','c'): {
  677                    value_SetEnum(parent,command_FieldId_func); ret = true; break;
  678                }
  679                case LE_STR4('g','c','o','v'): {
  680                    value_SetEnum(parent,command_FieldId_gcov); ret = true; break;
  681                }
  682                case LE_STR4('h','a','s','h'): {
  683                    value_SetEnum(parent,command_FieldId_hash); ret = true; break;
  684                }
  685                case LE_STR4('i','f','f','y'): {
  686                    value_SetEnum(parent,command_FieldId_iffy); ret = true; break;
  687                }
  688                case LE_STR4('i','f','m','t'): {
  689                    value_SetEnum(parent,command_FieldId_ifmt); ret = true; break;
  690                }
  691                case LE_STR4('j','c','d','b'): {
  692                    value_SetEnum(parent,command_FieldId_jcdb); ret = true; break;
  693                }
  694                case LE_STR4('j','o','b','s'): {
  695                    value_SetEnum(parent,command_FieldId_jobs); ret = true; break;
  696                }
  697                case LE_STR4('l','i','n','e'): {
  698                    value_SetEnum(parent,command_FieldId_line); ret = true; break;
  699                }
  700                case LE_STR4('l','i','n','k'): {
  701                    value_SetEnum(parent,command_FieldId_link); ret = true; break;
  702                }
  703                case LE_STR4('l','i','s','t'): {
  704                    value_SetEnum(parent,command_FieldId_list); ret = true; break;
  705                }
  706                case LE_STR4('l','o','g','s'): {
  707                    value_SetEnum(parent,command_FieldId_logs); ret = true; break;
  708                }
  709                case LE_STR4('m','d','b','g'): {
  710                    value_SetEnum(parent,command_FieldId_mdbg); ret = true; break;
  711                }
  712                case LE_STR4('m','d','b','l'): {
  713                    value_SetEnum(parent,command_FieldId_mdbl); ret = true; break;
  714                }
  715                case LE_STR4('m','e','t','a'): {
  716                    value_SetEnum(parent,command_FieldId_meta); ret = true; break;
  717                }
  718                case LE_STR4('m','n','u','m'): {
  719                    value_SetEnum(parent,command_FieldId_mnum); ret = true; break;
  720                }
  721                case LE_STR4('m','o','v','e'): {
  722                    value_SetEnum(parent,command_FieldId_move); ret = true; break;
  723                }
  724                case LE_STR4('m','s','t','r'): {
  725                    value_SetEnum(parent,command_FieldId_mstr); ret = true; break;
  726                }
  727                case LE_STR4('n','a','m','e'): {
  728                    value_SetEnum(parent,command_FieldId_name); ret = true; break;
  729                }
  730                case LE_STR4('n','c','m','d'): {
  731                    value_SetEnum(parent,command_FieldId_ncmd); ret = true; break;
  732                }
  733                case LE_STR4('n','m','s','g'): {
  734                    value_SetEnum(parent,command_FieldId_nmsg); ret = true; break;
  735                }
  736                case LE_STR4('n','o','t','e'): {
  737                    value_SetEnum(parent,command_FieldId_note); ret = true; break;
  738                }
  739                case LE_STR4('n','s','d','b'): {
  740                    value_SetEnum(parent,command_FieldId_nsdb); ret = true; break;
  741                }
  742                case LE_STR4('o','f','m','t'): {
  743                    value_SetEnum(parent,command_FieldId_ofmt); ret = true; break;
  744                }
  745                case LE_STR4('p','u','s','h'): {
  746                    value_SetEnum(parent,command_FieldId_push); ret = true; break;
  747                }
  748                case LE_STR4('r','e','f','s'): {
  749                    value_SetEnum(parent,command_FieldId_refs); ret = true; break;
  750                }
  751                case LE_STR4('r','o','o','t'): {
  752                    value_SetEnum(parent,command_FieldId_root); ret = true; break;
  753                }
  754                case LE_STR4('s','e','e','d'): {
  755                    value_SetEnum(parent,command_FieldId_seed); ret = true; break;
  756                }
  757                case LE_STR4('s','e','r','v'): {
  758                    value_SetEnum(parent,command_FieldId_serv); ret = true; break;
  759                }
  760                case LE_STR4('s','h','o','w'): {
  761                    value_SetEnum(parent,command_FieldId_show); ret = true; break;
  762                }
  763                case LE_STR4('s','i','t','e'): {
  764                    value_SetEnum(parent,command_FieldId_site); ret = true; break;
  765                }
  766                case LE_STR4('s','s','i','m'): {
  767                    value_SetEnum(parent,command_FieldId_ssim); ret = true; break;
  768                }
  769                case LE_STR4('s','t','a','t'): {
  770                    value_SetEnum(parent,command_FieldId_stat); ret = true; break;
  771                }
  772                case LE_STR4('s','t','a','y'): {
  773                    value_SetEnum(parent,command_FieldId_stay); ret = true; break;
  774                }
  775                case LE_STR4('s','t','o','p'): {
  776                    value_SetEnum(parent,command_FieldId_stop); ret = true; break;
  777                }
  778                case LE_STR4('s','y','n','c'): {
  779                    value_SetEnum(parent,command_FieldId_sync); ret = true; break;
  780                }
  781                case LE_STR4('t','e','s','t'): {
  782                    value_SetEnum(parent,command_FieldId_test); ret = true; break;
  783                }
  784                case LE_STR4('t','r','e','e'): {
  785                    value_SetEnum(parent,command_FieldId_tree); ret = true; break;
  786                }
  787                case LE_STR4('t','y','p','e'): {
  788                    value_SetEnum(parent,command_FieldId_type); ret = true; break;
  789                }
  790                case LE_STR4('u','n','d','o'): {
  791                    value_SetEnum(parent,command_FieldId_undo); ret = true; break;
  792                }
  793                case LE_STR4('u','s','e','r'): {
  794                    value_SetEnum(parent,command_FieldId_user); ret = true; break;
  795                }
  796                case LE_STR4('x','r','e','f'): {
  797                    value_SetEnum(parent,command_FieldId_xref); ret = true; break;
  798                }
  799            }
  800            break;
  801        }
  802        case 5: {
  803            switch (u64(algo::ReadLE32(rhs.elems))|(u64(rhs[4])<<32)) {
  804                case LE_STR5('a','b','o','r','t'): {
  805                    value_SetEnum(parent,command_FieldId_abort); ret = true; break;
  806                }
  807                case LE_STR5('a','f','l','a','g'): {
  808                    value_SetEnum(parent,command_FieldId_aflag); ret = true; break;
  809                }
  810                case LE_STR5('a','f','t','e','r'): {
  811                    value_SetEnum(parent,command_FieldId_after); ret = true; break;
  812                }
  813                case LE_STR5('a','l','i','a','s'): {
  814                    value_SetEnum(parent,command_FieldId_alias); ret = true; break;
  815                }
  816                case LE_STR5('a','m','n','u','m'): {
  817                    value_SetEnum(parent,command_FieldId_amnum); ret = true; break;
  818                }
  819                case LE_STR5('b','e','g','i','n'): {
  820                    value_SetEnum(parent,command_FieldId_begin); ret = true; break;
  821                }
  822                case LE_STR5('b','u','i','l','d'): {
  823                    value_SetEnum(parent,command_FieldId_build); ret = true; break;
  824                }
  825                case LE_STR5('c','a','c','h','e'): {
  826                    value_SetEnum(parent,command_FieldId_cache); ret = true; break;
  827                }
  828                case LE_STR5('c','h','e','c','k'): {
  829                    value_SetEnum(parent,command_FieldId_check); ret = true; break;
  830                }
  831                case LE_STR5('c','i','j','o','b'): {
  832                    value_SetEnum(parent,command_FieldId_cijob); ret = true; break;
  833                }
  834                case LE_STR5('c','l','e','a','n'): {
  835                    value_SetEnum(parent,command_FieldId_clean); ret = true; break;
  836                }
  837                case LE_STR5('c','t','y','p','e'): {
  838                    value_SetEnum(parent,command_FieldId_ctype); ret = true; break;
  839                }
  840                case LE_STR5('d','e','d','u','p'): {
  841                    value_SetEnum(parent,command_FieldId_dedup); ret = true; break;
  842                }
  843                case LE_STR5('d','f','l','a','g'): {
  844                    value_SetEnum(parent,command_FieldId_dflag); ret = true; break;
  845                }
  846                case LE_STR5('d','h','o','s','t'): {
  847                    value_SetEnum(parent,command_FieldId_dhost); ret = true; break;
  848                }
  849                case LE_STR5('d','i','s','a','s'): {
  850                    value_SetEnum(parent,command_FieldId_disas); ret = true; break;
  851                }
  852                case LE_STR5('d','p','k','e','y'): {
  853                    value_SetEnum(parent,command_FieldId_dpkey); ret = true; break;
  854                }
  855                case LE_STR5('d','r','e','g','x'): {
  856                    value_SetEnum(parent,command_FieldId_dregx); ret = true; break;
  857                }
  858                case LE_STR5('e','m','a','c','s'): {
  859                    value_SetEnum(parent,command_FieldId_emacs); ret = true; break;
  860                }
  861                case LE_STR5('f','i','e','l','d'): {
  862                    value_SetEnum(parent,command_FieldId_field); ret = true; break;
  863                }
  864                case LE_STR5('f','i','l','e','s'): {
  865                    value_SetEnum(parent,command_FieldId_files); ret = true; break;
  866                }
  867                case LE_STR5('f','n','a','m','e'): {
  868                    value_SetEnum(parent,command_FieldId_fname); ret = true; break;
  869                }
  870                case LE_STR5('f','o','r','c','e'): {
  871                    value_SetEnum(parent,command_FieldId_force); ret = true; break;
  872                }
  873                case LE_STR5('f','s','t','e','p'): {
  874                    value_SetEnum(parent,command_FieldId_fstep); ret = true; break;
  875                }
  876                case LE_STR5('l','o','c','a','l'): {
  877                    value_SetEnum(parent,command_FieldId_local); ret = true; break;
  878                }
  879                case LE_STR5('l','o','o','s','e'): {
  880                    value_SetEnum(parent,command_FieldId_loose); ret = true; break;
  881                }
  882                case LE_STR5('m','a','t','c','h'): {
  883                    value_SetEnum(parent,command_FieldId_match); ret = true; break;
  884                }
  885                case LE_STR5('m','e','r','g','e'): {
  886                    value_SetEnum(parent,command_FieldId_merge); ret = true; break;
  887                }
  888                case LE_STR5('m','s','i','z','e'): {
  889                    value_SetEnum(parent,command_FieldId_msize); ret = true; break;
  890                }
  891                case LE_STR5('n','d','o','w','n'): {
  892                    value_SetEnum(parent,command_FieldId_ndown); ret = true; break;
  893                }
  894                case LE_STR5('o','t','h','e','r'): {
  895                    value_SetEnum(parent,command_FieldId_other); ret = true; break;
  896                }
  897                case LE_STR5('p','o','i','n','t'): {
  898                    value_SetEnum(parent,command_FieldId_point); ret = true; break;
  899                }
  900                case LE_STR5('p','r','i','n','t'): {
  901                    value_SetEnum(parent,command_FieldId_print); ret = true; break;
  902                }
  903                case LE_STR5('p','r','o','t','o'): {
  904                    value_SetEnum(parent,command_FieldId_proto); ret = true; break;
  905                }
  906                case LE_STR5('p','u','r','g','e'): {
  907                    value_SetEnum(parent,command_FieldId_purge); ret = true; break;
  908                }
  909                case LE_STR5('q','u','e','r','y'): {
  910                    value_SetEnum(parent,command_FieldId_query); ret = true; break;
  911                }
  912                case LE_STR5('r','e','s','e','t'): {
  913                    value_SetEnum(parent,command_FieldId_reset); ret = true; break;
  914                }
  915                case LE_STR5('r','o','w','i','d'): {
  916                    value_SetEnum(parent,command_FieldId_rowid); ret = true; break;
  917                }
  918                case LE_STR5('s','e','r','v','e'): {
  919                    value_SetEnum(parent,command_FieldId_serve); ret = true; break;
  920                }
  921                case LE_STR5('s','h','e','l','l'): {
  922                    value_SetEnum(parent,command_FieldId_shell); ret = true; break;
  923                }
  924                case LE_STR5('s','t','a','r','t'): {
  925                    value_SetEnum(parent,command_FieldId_start); ret = true; break;
  926                }
  927                case LE_STR5('s','t','a','t','s'): {
  928                    value_SetEnum(parent,command_FieldId_stats); ret = true; break;
  929                }
  930                case LE_STR5('s','t','y','l','e'): {
  931                    value_SetEnum(parent,command_FieldId_style); ret = true; break;
  932                }
  933                case LE_STR5('t','r','a','c','e'): {
  934                    value_SetEnum(parent,command_FieldId_trace); ret = true; break;
  935                }
  936                case LE_STR5('t','r','u','n','c'): {
  937                    value_SetEnum(parent,command_FieldId_trunc); ret = true; break;
  938                }
  939                case LE_STR5('u','n','a','m','e'): {
  940                    value_SetEnum(parent,command_FieldId_uname); ret = true; break;
  941                }
  942                case LE_STR5('v','a','l','u','e'): {
  943                    value_SetEnum(parent,command_FieldId_value); ret = true; break;
  944                }
  945                case LE_STR5('w','h','e','r','e'): {
  946                    value_SetEnum(parent,command_FieldId_where); ret = true; break;
  947                }
  948                case LE_STR5('w','r','i','t','e'): {
  949                    value_SetEnum(parent,command_FieldId_write); ret = true; break;
  950                }
  951            }
  952            break;
  953        }
  954        case 6: {
  955            switch (u64(algo::ReadLE32(rhs.elems))|(u64(algo::ReadLE16(rhs.elems+4))<<32)) {
  956                case LE_STR6('a','c','c','e','p','t'): {
  957                    value_SetEnum(parent,command_FieldId_accept); ret = true; break;
  958                }
  959                case LE_STR6('a','d','m','s','v','c'): {
  960                    value_SetEnum(parent,command_FieldId_admsvc); ret = true; break;
  961                }
  962                case LE_STR6('a','n','c','h','o','r'): {
  963                    value_SetEnum(parent,command_FieldId_anchor); ret = true; break;
  964                }
  965                case LE_STR6('a','t','t','a','c','h'): {
  966                    value_SetEnum(parent,command_FieldId_attach); ret = true; break;
  967                }
  968                case LE_STR6('b','e','f','o','r','e'): {
  969                    value_SetEnum(parent,command_FieldId_before); ret = true; break;
  970                }
  971                case LE_STR6('b','i','g','e','n','d'): {
  972                    value_SetEnum(parent,command_FieldId_bigend); ret = true; break;
  973                }
  974                case LE_STR6('c','c','o','n','s','t'): {
  975                    value_SetEnum(parent,command_FieldId_cconst); ret = true; break;
  976                }
  977                case LE_STR6('c','i','t','e','s','t'): {
  978                    value_SetEnum(parent,command_FieldId_citest); ret = true; break;
  979                }
  980                case LE_STR6('c','o','m','m','i','t'): {
  981                    value_SetEnum(parent,command_FieldId_commit); ret = true; break;
  982                }
  983                case LE_STR6('c','o','v','d','i','r'): {
  984                    value_SetEnum(parent,command_FieldId_covdir); ret = true; break;
  985                }
  986                case LE_STR6('c','r','e','a','t','e'): {
  987                    value_SetEnum(parent,command_FieldId_create); ret = true; break;
  988                }
  989                case LE_STR6('d','e','c','o','d','e'): {
  990                    value_SetEnum(parent,command_FieldId_decode); ret = true; break;
  991                }
  992                case LE_STR6('d','o','f','o','r','k'): {
  993                    value_SetEnum(parent,command_FieldId_dofork); ret = true; break;
  994                }
  995                case LE_STR6('e','n','a','b','l','e'): {
  996                    value_SetEnum(parent,command_FieldId_enable); ret = true; break;
  997                }
  998                case LE_STR6('e','n','c','o','d','e'): {
  999                    value_SetEnum(parent,command_FieldId_encode); ret = true; break;
 1000                }
 1001                case LE_STR6('e','x','p','a','n','d'): {
 1002                    value_SetEnum(parent,command_FieldId_expand); ret = true; break;
 1003                }
 1004                case LE_STR6('f','c','o','n','s','t'): {
 1005                    value_SetEnum(parent,command_FieldId_fconst); ret = true; break;
 1006                }
 1007                case LE_STR6('f','i','e','l','d','s'): {
 1008                    value_SetEnum(parent,command_FieldId_fields); ret = true; break;
 1009                }
 1010                case LE_STR6('f','i','n','p','u','t'): {
 1011                    value_SetEnum(parent,command_FieldId_finput); ret = true; break;
 1012                }
 1013                case LE_STR6('f','i','x','_','b','s'): {
 1014                    value_SetEnum(parent,command_FieldId_fix_bs); ret = true; break;
 1015                }
 1016                case LE_STR6('f','i','x','_','n','l'): {
 1017                    value_SetEnum(parent,command_FieldId_fix_nl); ret = true; break;
 1018                }
 1019                case LE_STR6('f','o','r','m','a','t'): {
 1020                    value_SetEnum(parent,command_FieldId_format); ret = true; break;
 1021                }
 1022                case LE_STR6('g','e','n','a','m','c'): {
 1023                    value_SetEnum(parent,command_FieldId_genamc); ret = true; break;
 1024                }
 1025                case LE_STR6('g','i','t','d','i','r'): {
 1026                    value_SetEnum(parent,command_FieldId_gitdir); ret = true; break;
 1027                }
 1028                case LE_STR6('h','e','a','d','e','r'): {
 1029                    value_SetEnum(parent,command_FieldId_header); ret = true; break;
 1030                }
 1031                case LE_STR6('i','n','_','d','i','r'): {
 1032                    value_SetEnum(parent,command_FieldId_in_dir); ret = true; break;
 1033                }
 1034                case LE_STR6('i','n','d','e','n','t'): {
 1035                    value_SetEnum(parent,command_FieldId_indent); ret = true; break;
 1036                }
 1037                case LE_STR6('i','n','s','e','r','t'): {
 1038                    value_SetEnum(parent,command_FieldId_insert); ret = true; break;
 1039                }
 1040                case LE_STR6('m','a','x','e','r','r'): {
 1041                    value_SetEnum(parent,command_FieldId_maxerr); ret = true; break;
 1042                }
 1043                case LE_STR6('n','c','h','i','l','d'): {
 1044                    value_SetEnum(parent,command_FieldId_nchild); ret = true; break;
 1045                }
 1046                case LE_STR6('n','o','f','o','r','k'): {
 1047                    value_SetEnum(parent,command_FieldId_nofork); ret = true; break;
 1048                }
 1049                case LE_STR6('n','o','l','o','g','o'): {
 1050                    value_SetEnum(parent,command_FieldId_nologo); ret = true; break;
 1051                }
 1052                case LE_STR6('n','s','t','y','p','e'): {
 1053                    value_SetEnum(parent,command_FieldId_nstype); ret = true; break;
 1054                }
 1055                case LE_STR6('o','r','i','g','i','n'): {
 1056                    value_SetEnum(parent,command_FieldId_origin); ret = true; break;
 1057                }
 1058                case LE_STR6('p','r','e','f','i','x'): {
 1059                    value_SetEnum(parent,command_FieldId_prefix); ret = true; break;
 1060                }
 1061                case LE_STR6('p','r','e','t','t','y'): {
 1062                    value_SetEnum(parent,command_FieldId_pretty); ret = true; break;
 1063                }
 1064                case LE_STR6('r','e','a','d','m','e'): {
 1065                    value_SetEnum(parent,command_FieldId_readme); ret = true; break;
 1066                }
 1067                case LE_STR6('r','e','g','x','o','f'): {
 1068                    value_SetEnum(parent,command_FieldId_regxof); ret = true; break;
 1069                }
 1070                case LE_STR6('r','e','m','o','t','e'): {
 1071                    value_SetEnum(parent,command_FieldId_remote); ret = true; break;
 1072                }
 1073                case LE_STR6('r','e','m','o','v','e'): {
 1074                    value_SetEnum(parent,command_FieldId_remove); ret = true; break;
 1075                }
 1076                case LE_STR6('r','e','n','a','m','e'): {
 1077                    value_SetEnum(parent,command_FieldId_rename); ret = true; break;
 1078                }
 1079                case LE_STR6('r','e','n','d','e','r'): {
 1080                    value_SetEnum(parent,command_FieldId_render); ret = true; break;
 1081                }
 1082                case LE_STR6('r','e','p','o','r','t'): {
 1083                    value_SetEnum(parent,command_FieldId_report); ret = true; break;
 1084                }
 1085                case LE_STR6('r','u','n','c','m','d'): {
 1086                    value_SetEnum(parent,command_FieldId_runcmd); ret = true; break;
 1087                }
 1088                case LE_STR6('s','c','h','e','m','a'): {
 1089                    value_SetEnum(parent,command_FieldId_schema); ret = true; break;
 1090                }
 1091                case LE_STR6('s','e','l','e','r','r'): {
 1092                    value_SetEnum(parent,command_FieldId_selerr); ret = true; break;
 1093                }
 1094                case LE_STR6('s','t','r','e','a','m'): {
 1095                    value_SetEnum(parent,command_FieldId_stream); ret = true; break;
 1096                }
 1097                case LE_STR6('s','t','r','i','n','g'): {
 1098                    value_SetEnum(parent,command_FieldId_string); ret = true; break;
 1099                }
 1100                case LE_STR6('s','u','b','s','e','t'): {
 1101                    value_SetEnum(parent,command_FieldId_subset); ret = true; break;
 1102                }
 1103                case LE_STR6('s','u','b','s','t','r'): {
 1104                    value_SetEnum(parent,command_FieldId_substr); ret = true; break;
 1105                }
 1106                case LE_STR6('t','a','b','l','e','s'): {
 1107                    value_SetEnum(parent,command_FieldId_tables); ret = true; break;
 1108                }
 1109                case LE_STR6('t','a','r','g','e','t'): {
 1110                    value_SetEnum(parent,command_FieldId_target); ret = true; break;
 1111                }
 1112                case LE_STR6('u','n','u','s','e','d'): {
 1113                    value_SetEnum(parent,command_FieldId_unused); ret = true; break;
 1114                }
 1115                case LE_STR6('u','p','d','a','t','e'): {
 1116                    value_SetEnum(parent,command_FieldId_update); ret = true; break;
 1117                }
 1118                case LE_STR6('u','p','l','o','a','d'): {
 1119                    value_SetEnum(parent,command_FieldId_upload); ret = true; break;
 1120                }
 1121                case LE_STR6('v','e','r','i','f','y'): {
 1122                    value_SetEnum(parent,command_FieldId_verify); ret = true; break;
 1123                }
 1124            }
 1125            break;
 1126        }
 1127        case 7: {
 1128            switch (u64(algo::ReadLE32(rhs.elems))|(u64(algo::ReadLE16(rhs.elems+4))<<32)|(u64(rhs[6])<<48)) {
 1129                case LE_STR7('a','m','c','t','e','s','t'): {
 1130                    value_SetEnum(parent,command_FieldId_amctest); ret = true; break;
 1131                }
 1132                case LE_STR7('a','n','o','n','f','l','d'): {
 1133                    value_SetEnum(parent,command_FieldId_anonfld); ret = true; break;
 1134                }
 1135                case LE_STR7('a','p','p','r','o','v','e'): {
 1136                    value_SetEnum(parent,command_FieldId_approve); ret = true; break;
 1137                }
 1138                case LE_STR7('a','u','t','h','d','i','r'): {
 1139                    value_SetEnum(parent,command_FieldId_authdir); ret = true; break;
 1140                }
 1141                case LE_STR7('b','a','d','c','h','a','r'): {
 1142                    value_SetEnum(parent,command_FieldId_badchar); ret = true; break;
 1143                }
 1144                case LE_STR7('b','a','d','d','b','o','k'): {
 1145                    value_SetEnum(parent,command_FieldId_baddbok); ret = true; break;
 1146                }
 1147                case LE_STR7('b','a','d','d','e','c','l'): {
 1148                    value_SetEnum(parent,command_FieldId_baddecl); ret = true; break;
 1149                }
 1150                case LE_STR7('b','a','d','l','i','n','e'): {
 1151                    value_SetEnum(parent,command_FieldId_badline); ret = true; break;
 1152                }
 1153                case LE_STR7('b','i','n','p','a','t','h'): {
 1154                    value_SetEnum(parent,command_FieldId_binpath); ret = true; break;
 1155                }
 1156                case LE_STR7('b','u','f','s','i','z','e'): {
 1157                    value_SetEnum(parent,command_FieldId_bufsize); ret = true; break;
 1158                }
 1159                case LE_STR7('b','u','i','l','d','e','r'): {
 1160                    value_SetEnum(parent,command_FieldId_builder); ret = true; break;
 1161                }
 1162                case LE_STR7('c','a','p','t','u','r','e'): {
 1163                    value_SetEnum(parent,command_FieldId_capture); ret = true; break;
 1164                }
 1165                case LE_STR7('c','a','s','c','d','e','l'): {
 1166                    value_SetEnum(parent,command_FieldId_cascdel); ret = true; break;
 1167                }
 1168                case LE_STR7('c','l','o','n','e','t','o'): {
 1169                    value_SetEnum(parent,command_FieldId_cloneto); ret = true; break;
 1170                }
 1171                case LE_STR7('c','o','m','m','e','n','t'): {
 1172                    value_SetEnum(parent,command_FieldId_comment); ret = true; break;
 1173                }
 1174                case LE_STR7('c','o','m','p','d','i','r'): {
 1175                    value_SetEnum(parent,command_FieldId_compdir); ret = true; break;
 1176                }
 1177                case LE_STR7('c','p','p','f','u','n','c'): {
 1178                    value_SetEnum(parent,command_FieldId_cppfunc); ret = true; break;
 1179                }
 1180                case LE_STR7('d','a','t','a','_','i','n'): {
 1181                    value_SetEnum(parent,command_FieldId_data_in); ret = true; break;
 1182                }
 1183                case LE_STR7('d','i','s','a','b','l','e'): {
 1184                    value_SetEnum(parent,command_FieldId_disable); ret = true; break;
 1185                }
 1186                case LE_STR7('d','r','y','_','r','u','n'): {
 1187                    value_SetEnum(parent,command_FieldId_dry_run); ret = true; break;
 1188                }
 1189                case LE_STR7('e','n','v','n','o','d','e'): {
 1190                    value_SetEnum(parent,command_FieldId_envnode); ret = true; break;
 1191                }
 1192                case LE_STR7('e','x','c','l','u','d','e'): {
 1193                    value_SetEnum(parent,command_FieldId_exclude); ret = true; break;
 1194                }
 1195                case LE_STR7('f','i','x','_','s','o','h'): {
 1196                    value_SetEnum(parent,command_FieldId_fix_soh); ret = true; break;
 1197                }
 1198                case LE_STR7('f','l','d','f','u','n','c'): {
 1199                    value_SetEnum(parent,command_FieldId_fldfunc); ret = true; break;
 1200                }
 1201                case LE_STR7('f','o','u','t','p','u','t'): {
 1202                    value_SetEnum(parent,command_FieldId_foutput); ret = true; break;
 1203                }
 1204                case LE_STR7('g','m','v','p','r','o','j'): {
 1205                    value_SetEnum(parent,command_FieldId_gmvproj); ret = true; break;
 1206                }
 1207                case LE_STR7('g','s','t','a','t','i','c'): {
 1208                    value_SetEnum(parent,command_FieldId_gstatic); ret = true; break;
 1209                }
 1210                case LE_STR7('h','a','s','h','f','l','d'): {
 1211                    value_SetEnum(parent,command_FieldId_hashfld); ret = true; break;
 1212                }
 1213                case LE_STR7('h','i','t','r','a','t','e'): {
 1214                    value_SetEnum(parent,command_FieldId_hitrate); ret = true; break;
 1215                }
 1216                case LE_STR7('i','n','c','l','u','d','e'): {
 1217                    value_SetEnum(parent,command_FieldId_include); ret = true; break;
 1218                }
 1219                case LE_STR7('i','n','d','e','x','e','d'): {
 1220                    value_SetEnum(parent,command_FieldId_indexed); ret = true; break;
 1221                }
 1222                case LE_STR7('i','n','s','c','o','n','d'): {
 1223                    value_SetEnum(parent,command_FieldId_inscond); ret = true; break;
 1224                }
 1225                case LE_STR7('i','n','s','p','e','c','t'): {
 1226                    value_SetEnum(parent,command_FieldId_inspect); ret = true; break;
 1227                }
 1228                case LE_STR7('i','n','s','t','a','l','l'): {
 1229                    value_SetEnum(parent,command_FieldId_install); ret = true; break;
 1230                }
 1231                case LE_STR7('l','i','n','e','l','i','m'): {
 1232                    value_SetEnum(parent,command_FieldId_linelim); ret = true; break;
 1233                }
 1234                case LE_STR7('l','o','g','f','i','l','e'): {
 1235                    value_SetEnum(parent,command_FieldId_logfile); ret = true; break;
 1236                }
 1237                case LE_STR7('m','a','n','y','w','i','n'): {
 1238                    value_SetEnum(parent,command_FieldId_manywin); ret = true; break;
 1239                }
 1240                case LE_STR7('m','a','x','j','o','b','s'): {
 1241                    value_SetEnum(parent,command_FieldId_maxjobs); ret = true; break;
 1242                }
 1243                case LE_STR7('m','a','x','s','h','o','w'): {
 1244                    value_SetEnum(parent,command_FieldId_maxshow); ret = true; break;
 1245                }
 1246                case LE_STR7('m','a','x','t','i','m','e'): {
 1247                    value_SetEnum(parent,command_FieldId_maxtime); ret = true; break;
 1248                }
 1249                case LE_STR7('m','o','v','e','_','i','n'): {
 1250                    value_SetEnum(parent,command_FieldId_move_in); ret = true; break;
 1251                }
 1252                case LE_STR7('m','s','g','t','y','p','e'): {
 1253                    value_SetEnum(parent,command_FieldId_msgtype); ret = true; break;
 1254                }
 1255                case LE_STR7('n','o','i','n','p','u','t'): {
 1256                    value_SetEnum(parent,command_FieldId_noinput); ret = true; break;
 1257                }
 1258                case LE_STR7('o','u','t','_','d','i','r'): {
 1259                    value_SetEnum(parent,command_FieldId_out_dir); ret = true; break;
 1260                }
 1261                case LE_STR7('p','a','c','k','a','g','e'): {
 1262                    value_SetEnum(parent,command_FieldId_package); ret = true; break;
 1263                }
 1264                case LE_STR7('p','a','g','e','d','i','r'): {
 1265                    value_SetEnum(parent,command_FieldId_pagedir); ret = true; break;
 1266                }
 1267                case LE_STR7('p','k','g','d','a','t','a'): {
 1268                    value_SetEnum(parent,command_FieldId_pkgdata); ret = true; break;
 1269                }
 1270                case LE_STR7('p','r','e','p','r','o','c'): {
 1271                    value_SetEnum(parent,command_FieldId_preproc); ret = true; break;
 1272                }
 1273                case LE_STR7('r','e','f','t','y','p','e'): {
 1274                    value_SetEnum(parent,command_FieldId_reftype); ret = true; break;
 1275                }
 1276                case LE_STR7('r','e','l','a','t','e','d'): {
 1277                    value_SetEnum(parent,command_FieldId_related); ret = true; break;
 1278                }
 1279                case LE_STR7('r','e','p','l','a','c','e'): {
 1280                    value_SetEnum(parent,command_FieldId_replace); ret = true; break;
 1281                }
 1282                case LE_STR7('s','a','n','d','b','o','x'): {
 1283                    value_SetEnum(parent,command_FieldId_sandbox); ret = true; break;
 1284                }
 1285                case LE_STR7('s','e','c','t','i','o','n'): {
 1286                    value_SetEnum(parent,command_FieldId_section); ret = true; break;
 1287                }
 1288                case LE_STR7('s','h','o','w','c','p','p'): {
 1289                    value_SetEnum(parent,command_FieldId_showcpp); ret = true; break;
 1290                }
 1291                case LE_STR7('s','h','o','w','l','o','c'): {
 1292                    value_SetEnum(parent,command_FieldId_showloc); ret = true; break;
 1293                }
 1294                case LE_STR7('s','h','o','w','r','e','c'): {
 1295                    value_SetEnum(parent,command_FieldId_showrec); ret = true; break;
 1296                }
 1297                case LE_STR7('s','o','r','t','f','l','d'): {
 1298                    value_SetEnum(parent,command_FieldId_sortfld); ret = true; break;
 1299                }
 1300                case LE_STR7('s','r','c','f','i','l','e'): {
 1301                    value_SetEnum(parent,command_FieldId_srcfile); ret = true; break;
 1302                }
 1303                case LE_STR7('s','u','b','s','e','t','2'): {
 1304                    value_SetEnum(parent,command_FieldId_subset2); ret = true; break;
 1305                }
 1306                case LE_STR7('s','u','m','m','a','r','y'): {
 1307                    value_SetEnum(parent,command_FieldId_summary); ret = true; break;
 1308                }
 1309                case LE_STR7('t','a','r','g','s','r','c'): {
 1310                    value_SetEnum(parent,command_FieldId_targsrc); ret = true; break;
 1311                }
 1312                case LE_STR7('t','i','m','e','o','u','t'): {
 1313                    value_SetEnum(parent,command_FieldId_timeout); ret = true; break;
 1314                }
 1315                case LE_STR7('t','y','p','e','t','a','g'): {
 1316                    value_SetEnum(parent,command_FieldId_typetag); ret = true; break;
 1317                }
 1318                case LE_STR7('w','o','r','k','d','i','r'): {
 1319                    value_SetEnum(parent,command_FieldId_workdir); ret = true; break;
 1320                }
 1321            }
 1322            break;
 1323        }
 1324        case 8: {
 1325            switch (algo::ReadLE64(rhs.elems)) {
 1326                case LE_STR8('a','n','n','o','t','a','t','e'): {
 1327                    value_SetEnum(parent,command_FieldId_annotate); ret = true; break;
 1328                }
 1329                case LE_STR8('b','l','o','c','k','i','n','g'): {
 1330                    value_SetEnum(parent,command_FieldId_blocking); ret = true; break;
 1331                }
 1332                case LE_STR8('c','m','d','_','d','c','t','r'): {
 1333                    value_SetEnum(parent,command_FieldId_cmd_dctr); ret = true; break;
 1334                }
 1335                case LE_STR8('c','o','m','p','i','l','e','r'): {
 1336                    value_SetEnum(parent,command_FieldId_compiler); ret = true; break;
 1337                }
 1338                case LE_STR8('c','o','m','p','t','e','s','t'): {
 1339                    value_SetEnum(parent,command_FieldId_comptest); ret = true; break;
 1340                }
 1341                case LE_STR8('c','o','v','c','h','e','c','k'): {
 1342                    value_SetEnum(parent,command_FieldId_covcheck); ret = true; break;
 1343                }
 1344                case LE_STR8('c','o','v','e','r','i','t','y'): {
 1345                    value_SetEnum(parent,command_FieldId_coverity); ret = true; break;
 1346                }
 1347                case LE_STR8('c','r','e','a','t','e','d','b'): {
 1348                    value_SetEnum(parent,command_FieldId_createdb); ret = true; break;
 1349                }
 1350                case LE_STR8('d','a','t','a','_','d','i','r'): {
 1351                    value_SetEnum(parent,command_FieldId_data_dir); ret = true; break;
 1352                }
 1353                case LE_STR8('d','c','t','r','h','o','s','t'): {
 1354                    value_SetEnum(parent,command_FieldId_dctrhost); ret = true; break;
 1355                }
 1356                case LE_STR8('d','h','o','s','t','_','i','p'): {
 1357                    value_SetEnum(parent,command_FieldId_dhost_ip); ret = true; break;
 1358                }
 1359                case LE_STR8('d','o','w','n','l','o','a','d'): {
 1360                    value_SetEnum(parent,command_FieldId_download); ret = true; break;
 1361                }
 1362                case LE_STR8('g','e','n','e','r','a','t','e'): {
 1363                    value_SetEnum(parent,command_FieldId_generate); ret = true; break;
 1364                }
 1365                case LE_STR8('l','i','s','t','f','u','n','c'): {
 1366                    value_SetEnum(parent,command_FieldId_listfunc); ret = true; break;
 1367                }
 1368                case LE_STR8('l','i','s','t','i','n','c','l'): {
 1369                    value_SetEnum(parent,command_FieldId_listincl); ret = true; break;
 1370                }
 1371                case LE_STR8('m','a','x','g','r','o','u','p'): {
 1372                    value_SetEnum(parent,command_FieldId_maxgroup); ret = true; break;
 1373                }
 1374                case LE_STR8('m','e','m','c','h','e','c','k'): {
 1375                    value_SetEnum(parent,command_FieldId_memcheck); ret = true; break;
 1376                }
 1377                case LE_STR8('m','o','v','e','_','o','u','t'): {
 1378                    value_SetEnum(parent,command_FieldId_move_out); ret = true; break;
 1379                }
 1380                case LE_STR8('n','e','x','t','f','i','l','e'): {
 1381                    value_SetEnum(parent,command_FieldId_nextfile); ret = true; break;
 1382                }
 1383                case LE_STR8('n','u','l','l','a','b','l','e'): {
 1384                    value_SetEnum(parent,command_FieldId_nullable); ret = true; break;
 1385                }
 1386                case LE_STR8('p','a','t','h','c','o','m','p'): {
 1387                    value_SetEnum(parent,command_FieldId_pathcomp); ret = true; break;
 1388                }
 1389                case LE_STR8('p','o','o','l','t','y','p','e'): {
 1390                    value_SetEnum(parent,command_FieldId_pooltype); ret = true; break;
 1391                }
 1392                case LE_STR8('p','r','i','n','t','c','m','d'): {
 1393                    value_SetEnum(parent,command_FieldId_printcmd); ret = true; break;
 1394                }
 1395                case LE_STR8('r','e','n','a','m','e','t','o'): {
 1396                    value_SetEnum(parent,command_FieldId_renameto); ret = true; break;
 1397                }
 1398                case LE_STR8('s','e','l','e','c','t','o','r'): {
 1399                    value_SetEnum(parent,command_FieldId_selector); ret = true; break;
 1400                }
 1401                case LE_STR8('s','h','o','w','f','i','l','e'): {
 1402                    value_SetEnum(parent,command_FieldId_showfile); ret = true; break;
 1403                }
 1404                case LE_STR8('s','i','g','c','h','e','c','k'): {
 1405                    value_SetEnum(parent,command_FieldId_sigcheck); ret = true; break;
 1406                }
 1407                case LE_STR8('s','o','r','t','n','a','m','e'): {
 1408                    value_SetEnum(parent,command_FieldId_sortname); ret = true; break;
 1409                }
 1410                case LE_STR8('s','r','c','f','i','e','l','d'): {
 1411                    value_SetEnum(parent,command_FieldId_srcfield); ret = true; break;
 1412                }
 1413                case LE_STR8('s','s','i','m','f','i','l','e'): {
 1414                    value_SetEnum(parent,command_FieldId_ssimfile); ret = true; break;
 1415                }
 1416                case LE_STR8('t','e','s','t','p','r','o','b'): {
 1417                    value_SetEnum(parent,command_FieldId_testprob); ret = true; break;
 1418                }
 1419                case LE_STR8('u','n','i','t','t','e','s','t'): {
 1420                    value_SetEnum(parent,command_FieldId_unittest); ret = true; break;
 1421                }
 1422                case LE_STR8('w','i','k','i','s','i','t','e'): {
 1423                    value_SetEnum(parent,command_FieldId_wikisite); ret = true; break;
 1424                }
 1425            }
 1426            break;
 1427        }
 1428        case 9: {
 1429            switch (algo::ReadLE64(rhs.elems)) {
 1430                case LE_STR8('a','d','d','_','r','e','s','e'): {
 1431                    if (memcmp(rhs.elems+8,"t",1)==0) { value_SetEnum(parent,command_FieldId_add_reset); ret = true; break; }
 1432                    break;
 1433                }
 1434                case LE_STR8('c','a','l','l','g','r','i','n'): {
 1435                    if (memcmp(rhs.elems+8,"d",1)==0) { value_SetEnum(parent,command_FieldId_callgrind); ret = true; break; }
 1436                    break;
 1437                }
 1438                case LE_STR8('c','h','e','c','k','a','b','l'): {
 1439                    if (memcmp(rhs.elems+8,"e",1)==0) { value_SetEnum(parent,command_FieldId_checkable); ret = true; break; }
 1440                    break;
 1441                }
 1442                case LE_STR8('c','l','e','a','n','_','r','u'): {
 1443                    if (memcmp(rhs.elems+8,"n",1)==0) { value_SetEnum(parent,command_FieldId_clean_run); ret = true; break; }
 1444                    break;
 1445                }
 1446                case LE_STR8('d','c','t','r','_','f','r','o'): {
 1447                    if (memcmp(rhs.elems+8,"m",1)==0) { value_SetEnum(parent,command_FieldId_dctr_from); ret = true; break; }
 1448                    break;
 1449                }
 1450                case LE_STR8('d','e','b','u','g','_','l','o'): {
 1451                    if (memcmp(rhs.elems+8,"g",1)==0) { value_SetEnum(parent,command_FieldId_debug_log); ret = true; break; }
 1452                    break;
 1453                }
 1454                case LE_STR8('f','u','z','z','s','t','r','a'): {
 1455                    if (memcmp(rhs.elems+8,"t",1)==0) { value_SetEnum(parent,command_FieldId_fuzzstrat); ret = true; break; }
 1456                    break;
 1457                }
 1458                case LE_STR8('i','n','p','u','t','f','i','l'): {
 1459                    if (memcmp(rhs.elems+8,"e",1)==0) { value_SetEnum(parent,command_FieldId_inputfile); ret = true; break; }
 1460                    break;
 1461                }
 1462                case LE_STR8('m','a','x','p','a','c','k','e'): {
 1463                    if (memcmp(rhs.elems+8,"t",1)==0) { value_SetEnum(parent,command_FieldId_maxpacket); ret = true; break; }
 1464                    break;
 1465                }
 1466                case LE_STR8('m','e','r','g','e','p','a','t'): {
 1467                    if (memcmp(rhs.elems+8,"h",1)==0) { value_SetEnum(parent,command_FieldId_mergepath); ret = true; break; }
 1468                    break;
 1469                }
 1470                case LE_STR8('n','o','_','r','e','b','a','s'): {
 1471                    if (memcmp(rhs.elems+8,"e",1)==0) { value_SetEnum(parent,command_FieldId_no_rebase); ret = true; break; }
 1472                    break;
 1473                }
 1474                case LE_STR8('n','o','r','m','a','l','i','z'): {
 1475                    if (memcmp(rhs.elems+8,"e",1)==0) { value_SetEnum(parent,command_FieldId_normalize); ret = true; break; }
 1476                    break;
 1477                }
 1478                case LE_STR8('p','e','r','f','_','s','e','c'): {
 1479                    if (memcmp(rhs.elems+8,"s",1)==0) { value_SetEnum(parent,command_FieldId_perf_secs); ret = true; break; }
 1480                    break;
 1481                }
 1482                case LE_STR8('r','e','c','v','d','e','l','a'): {
 1483                    if (memcmp(rhs.elems+8,"y",1)==0) { value_SetEnum(parent,command_FieldId_recvdelay); ret = true; break; }
 1484                    break;
 1485                }
 1486                case LE_STR8('r','e','p','r','o','f','i','l'): {
 1487                    if (memcmp(rhs.elems+8,"e",1)==0) { value_SetEnum(parent,command_FieldId_reprofile); ret = true; break; }
 1488                    break;
 1489                }
 1490                case LE_STR8('r','s','y','n','c','_','g','e'): {
 1491                    if (memcmp(rhs.elems+8,"t",1)==0) { value_SetEnum(parent,command_FieldId_rsync_get); ret = true; break; }
 1492                    break;
 1493                }
 1494                case LE_STR8('r','s','y','n','c','_','p','u'): {
 1495                    if (memcmp(rhs.elems+8,"t",1)==0) { value_SetEnum(parent,command_FieldId_rsync_put); ret = true; break; }
 1496                    break;
 1497                }
 1498                case LE_STR8('s','e','p','a','r','a','t','o'): {
 1499                    if (memcmp(rhs.elems+8,"r",1)==0) { value_SetEnum(parent,command_FieldId_separator); ret = true; break; }
 1500                    break;
 1501                }
 1502                case LE_STR8('s','k','i','p','_','i','n','i'): {
 1503                    if (memcmp(rhs.elems+8,"t",1)==0) { value_SetEnum(parent,command_FieldId_skip_init); ret = true; break; }
 1504                    break;
 1505                }
 1506                case LE_STR8('s','t','r','a','y','f','i','l'): {
 1507                    if (memcmp(rhs.elems+8,"e",1)==0) { value_SetEnum(parent,command_FieldId_strayfile); ret = true; break; }
 1508                    break;
 1509                }
 1510                case LE_STR8('w','i','k','i','_','k','i','l'): {
 1511                    if (memcmp(rhs.elems+8,"l",1)==0) { value_SetEnum(parent,command_FieldId_wiki_kill); ret = true; break; }
 1512                    break;
 1513                }
 1514                case LE_STR8('x','m','l','p','r','e','t','t'): {
 1515                    if (memcmp(rhs.elems+8,"y",1)==0) { value_SetEnum(parent,command_FieldId_xmlpretty); ret = true; break; }
 1516                    break;
 1517                }
 1518            }
 1519            break;
 1520        }
 1521        case 10: {
 1522            switch (algo::ReadLE64(rhs.elems)) {
 1523                case LE_STR8('c','a','t','c','h','t','h','r'): {
 1524                    if (memcmp(rhs.elems+8,"ow",2)==0) { value_SetEnum(parent,command_FieldId_catchthrow); ret = true; break; }
 1525                    break;
 1526                }
 1527                case LE_STR8('c','h','e','c','k','c','l','e'): {
 1528                    if (memcmp(rhs.elems+8,"an",2)==0) { value_SetEnum(parent,command_FieldId_checkclean); ret = true; break; }
 1529                    break;
 1530                }
 1531                case LE_STR8('c','o','n','f','i','g','_','s'): {
 1532                    if (memcmp(rhs.elems+8,"sh",2)==0) { value_SetEnum(parent,command_FieldId_config_ssh); ret = true; break; }
 1533                    break;
 1534                }
 1535                case LE_STR8('c','o','v','c','a','p','t','u'): {
 1536                    if (memcmp(rhs.elems+8,"re",2)==0) { value_SetEnum(parent,command_FieldId_covcapture); ret = true; break; }
 1537                    break;
 1538                }
 1539                case LE_STR8('d','h','o','s','t','_','u','s'): {
 1540                    if (memcmp(rhs.elems+8,"er",2)==0) { value_SetEnum(parent,command_FieldId_dhost_user); ret = true; break; }
 1541                    break;
 1542                }
 1543                case LE_STR8('g','t','b','l','a','c','t','t'): {
 1544                    if (memcmp(rhs.elems+8,"st",2)==0) { value_SetEnum(parent,command_FieldId_gtblacttst); ret = true; break; }
 1545                    break;
 1546                }
 1547                case LE_STR8('k','e','e','p','_','f','i','l'): {
 1548                    if (memcmp(rhs.elems+8,"es",2)==0) { value_SetEnum(parent,command_FieldId_keep_files); ret = true; break; }
 1549                    break;
 1550                }
 1551                case LE_STR8('n','e','e','d','s','_','w','o'): {
 1552                    if (memcmp(rhs.elems+8,"rk",2)==0) { value_SetEnum(parent,command_FieldId_needs_work); ret = true; break; }
 1553                    break;
 1554                }
 1555                case LE_STR8('p','r','i','n','t','i','n','p'): {
 1556                    if (memcmp(rhs.elems+8,"ut",2)==0) { value_SetEnum(parent,command_FieldId_printinput); ret = true; break; }
 1557                    break;
 1558                }
 1559                case LE_STR8('r','s','y','n','c','_','o','p'): {
 1560                    if (memcmp(rhs.elems+8,"ts",2)==0) { value_SetEnum(parent,command_FieldId_rsync_opts); ret = true; break; }
 1561                    break;
 1562                }
 1563                case LE_STR8('s','c','r','i','p','t','_','d'): {
 1564                    if (memcmp(rhs.elems+8,"ir",2)==0) { value_SetEnum(parent,command_FieldId_script_dir); ret = true; break; }
 1565                    break;
 1566                }
 1567                case LE_STR8('s','c','r','i','p','t','f','i'): {
 1568                    if (memcmp(rhs.elems+8,"le",2)==0) { value_SetEnum(parent,command_FieldId_scriptfile); ret = true; break; }
 1569                    break;
 1570                }
 1571                case LE_STR8('s','h','o','w','s','t','a','t'): {
 1572                    if (memcmp(rhs.elems+8,"ic",2)==0) { value_SetEnum(parent,command_FieldId_showstatic); ret = true; break; }
 1573                    break;
 1574                }
 1575                case LE_STR8('w','i','k','i','_','s','t','a'): {
 1576                    if (memcmp(rhs.elems+8,"rt",2)==0) { value_SetEnum(parent,command_FieldId_wiki_start); ret = true; break; }
 1577                    break;
 1578                }
 1579                case LE_STR8('w','r','i','t','e','_','o','u'): {
 1580                    if (memcmp(rhs.elems+8,"rs",2)==0) { value_SetEnum(parent,command_FieldId_write_ours); ret = true; break; }
 1581                    break;
 1582                }
 1583                case LE_STR8('w','t','b','l','a','c','t','t'): {
 1584                    if (memcmp(rhs.elems+8,"st",2)==0) { value_SetEnum(parent,command_FieldId_wtblacttst); ret = true; break; }
 1585                    break;
 1586                }
 1587            }
 1588            break;
 1589        }
 1590        case 11: {
 1591            switch (algo::ReadLE64(rhs.elems)) {
 1592                case LE_STR8('b','u','i','l','d','_','q','u'): {
 1593                    if (memcmp(rhs.elems+8,"iet",3)==0) { value_SetEnum(parent,command_FieldId_build_quiet); ret = true; break; }
 1594                    break;
 1595                }
 1596                case LE_STR8('f','i','l','e','_','p','r','e'): {
 1597                    if (memcmp(rhs.elems+8,"fix",3)==0) { value_SetEnum(parent,command_FieldId_file_prefix); ret = true; break; }
 1598                    break;
 1599                }
 1600                case LE_STR8('i','g','n','o','r','e','Q','u'): {
 1601                    if (memcmp(rhs.elems+8,"ote",3)==0) { value_SetEnum(parent,command_FieldId_ignoreQuote); ret = true; break; }
 1602                    break;
 1603                }
 1604                case LE_STR8('m','s','g','s','i','z','e','_'): {
 1605                    if (memcmp(rhs.elems+8,"min",3)==0) { value_SetEnum(parent,command_FieldId_msgsize_min); ret = true; break; }
 1606                    if (memcmp(rhs.elems+8,"max",3)==0) { value_SetEnum(parent,command_FieldId_msgsize_max); ret = true; break; }
 1607                    break;
 1608                }
 1609                case LE_STR8('n','o','t','s','s','i','m','f'): {
 1610                    if (memcmp(rhs.elems+8,"ile",3)==0) { value_SetEnum(parent,command_FieldId_notssimfile); ret = true; break; }
 1611                    break;
 1612                }
 1613                case LE_STR8('s','e','r','v','e','_','p','a'): {
 1614                    if (memcmp(rhs.elems+8,"ges",3)==0) { value_SetEnum(parent,command_FieldId_serve_pages); ret = true; break; }
 1615                    break;
 1616                }
 1617                case LE_STR8('s','h','o','w','s','o','r','t'): {
 1618                    if (memcmp(rhs.elems+8,"key",3)==0) { value_SetEnum(parent,command_FieldId_showsortkey); ret = true; break; }
 1619                    break;
 1620                }
 1621                case LE_STR8('t','o','c','a','m','e','l','c'): {
 1622                    if (memcmp(rhs.elems+8,"ase",3)==0) { value_SetEnum(parent,command_FieldId_tocamelcase); ret = true; break; }
 1623                    break;
 1624                }
 1625                case LE_STR8('u','p','d','a','t','e','p','r'): {
 1626                    if (memcmp(rhs.elems+8,"oto",3)==0) { value_SetEnum(parent,command_FieldId_updateproto); ret = true; break; }
 1627                    break;
 1628                }
 1629                case LE_STR8('w','i','k','i','_','u','p','d'): {
 1630                    if (memcmp(rhs.elems+8,"ate",3)==0) { value_SetEnum(parent,command_FieldId_wiki_update); ret = true; break; }
 1631                    break;
 1632                }
 1633            }
 1634            break;
 1635        }
 1636        case 12: {
 1637            switch (algo::ReadLE64(rhs.elems)) {
 1638                case LE_STR8('a','u','t','h','d','i','r','_'): {
 1639                    if (memcmp(rhs.elems+8,"dflt",4)==0) { value_SetEnum(parent,command_FieldId_authdir_dflt); ret = true; break; }
 1640                    break;
 1641                }
 1642                case LE_STR8('d','h','o','s','t','_','e','x'): {
 1643                    if (memcmp(rhs.elems+8,"t_ip",4)==0) { value_SetEnum(parent,command_FieldId_dhost_ext_ip); ret = true; break; }
 1644                    break;
 1645                }
 1646                case LE_STR8('f','a','s','t','_','m','s','g'): {
 1647                    if (memcmp(rhs.elems+8,"_max",4)==0) { value_SetEnum(parent,command_FieldId_fast_msg_max); ret = true; break; }
 1648                    break;
 1649                }
 1650                case LE_STR8('f','o','l','l','o','w','_','c'): {
 1651                    if (memcmp(rhs.elems+8,"hild",4)==0) { value_SetEnum(parent,command_FieldId_follow_child); ret = true; break; }
 1652                    break;
 1653                }
 1654                case LE_STR8('o','u','t','s','e','p','a','r'): {
 1655                    if (memcmp(rhs.elems+8,"ator",4)==0) { value_SetEnum(parent,command_FieldId_outseparator); ret = true; break; }
 1656                    break;
 1657                }
 1658                case LE_STR8('r','e','c','v','d','e','l','a'): {
 1659                    if (memcmp(rhs.elems+8,"y_ns",4)==0) { value_SetEnum(parent,command_FieldId_recvdelay_ns); ret = true; break; }
 1660                    break;
 1661                }
 1662                case LE_STR8('s','e','n','d','d','e','l','a'): {
 1663                    if (memcmp(rhs.elems+8,"y_ns",4)==0) { value_SetEnum(parent,command_FieldId_senddelay_ns); ret = true; break; }
 1664                    break;
 1665                }
 1666                case LE_STR8('s','e','r','v','e','_','v','e'): {
 1667                    if (memcmp(rhs.elems+8,"rify",4)==0) { value_SetEnum(parent,command_FieldId_serve_verify); ret = true; break; }
 1668                    break;
 1669                }
 1670                case LE_STR8('t','o','l','o','w','e','r','u'): {
 1671                    if (memcmp(rhs.elems+8,"nder",4)==0) { value_SetEnum(parent,command_FieldId_tolowerunder); ret = true; break; }
 1672                    break;
 1673                }
 1674                case LE_STR8('w','i','k','i','_','e','n','v'): {
 1675                    if (memcmp(rhs.elems+8,"node",4)==0) { value_SetEnum(parent,command_FieldId_wiki_envnode); ret = true; break; }
 1676                    break;
 1677                }
 1678            }
 1679            break;
 1680        }
 1681        case 13: {
 1682            switch (algo::ReadLE64(rhs.elems)) {
 1683                case LE_STR8('o','p','t','s','_','n','u','m'): {
 1684                    if (memcmp(rhs.elems+8,"bered",5)==0) { value_SetEnum(parent,command_FieldId_opts_numbered); ret = true; break; }
 1685                    break;
 1686                }
 1687                case LE_STR8('p','r','e','f','e','r','_','s'): {
 1688                    if (memcmp(rhs.elems+8,"igned",5)==0) { value_SetEnum(parent,command_FieldId_prefer_signed); ret = true; break; }
 1689                    break;
 1690                }
 1691                case LE_STR8('s','k','i','p','_','g','i','t'): {
 1692                    if (memcmp(rhs.elems+8,"_init",5)==0) { value_SetEnum(parent,command_FieldId_skip_git_init); ret = true; break; }
 1693                    break;
 1694                }
 1695                case LE_STR8('w','r','i','t','e','s','s','i'): {
 1696                    if (memcmp(rhs.elems+8,"mfile",5)==0) { value_SetEnum(parent,command_FieldId_writessimfile); ret = true; break; }
 1697                    break;
 1698                }
 1699            }
 1700            break;
 1701        }
 1702        case 14: {
 1703            switch (algo::ReadLE64(rhs.elems)) {
 1704                case LE_STR8('c','o','n','f','i','g','_','s'): {
 1705                    if (memcmp(rhs.elems+8,"sh_vpn",6)==0) { value_SetEnum(parent,command_FieldId_config_ssh_vpn); ret = true; break; }
 1706                    break;
 1707                }
 1708                case LE_STR8('d','h','o','s','t','_','e','x'): {
 1709                    if (memcmp(rhs.elems+8,"t_port",6)==0) { value_SetEnum(parent,command_FieldId_dhost_ext_port); ret = true; break; }
 1710                    break;
 1711                }
 1712                case LE_STR8('g','e','n','e','r','a','t','e'): {
 1713                    if (memcmp(rhs.elems+8,"_build",6)==0) { value_SetEnum(parent,command_FieldId_generate_build); ret = true; break; }
 1714                    break;
 1715                }
 1716                case LE_STR8('s','e','r','v','e','_','d','c'): {
 1717                    if (memcmp(rhs.elems+8,"trhost",6)==0) { value_SetEnum(parent,command_FieldId_serve_dctrhost); ret = true; break; }
 1718                    break;
 1719                }
 1720            }
 1721            break;
 1722        }
 1723        case 15: {
 1724            switch (algo::ReadLE64(rhs.elems)) {
 1725                case LE_STR8('c','h','e','c','k','_','u','n'): {
 1726                    if (memcmp(rhs.elems+8,"tracked",7)==0) { value_SetEnum(parent,command_FieldId_check_untracked); ret = true; break; }
 1727                    break;
 1728                }
 1729                case LE_STR8('p','e','r','t','e','s','t','_'): {
 1730                    if (memcmp(rhs.elems+8,"timeout",7)==0) { value_SetEnum(parent,command_FieldId_pertest_timeout); ret = true; break; }
 1731                    break;
 1732                }
 1733                case LE_STR8('p','r','e','s','e','r','v','e'): {
 1734                    if (memcmp(rhs.elems+8,"_footer",7)==0) { value_SetEnum(parent,command_FieldId_preserve_footer); ret = true; break; }
 1735                    break;
 1736                }
 1737                case LE_STR8('r','a','n','d','o','m','i','z'): {
 1738                    if (memcmp(rhs.elems+8,"e_ports",7)==0) { value_SetEnum(parent,command_FieldId_randomize_ports); ret = true; break; }
 1739                    break;
 1740                }
 1741            }
 1742            break;
 1743        }
 1744        case 16: {
 1745            switch (algo::ReadLE64(rhs.elems)) {
 1746                case LE_STR8('e','x','c','l','u','d','e','_'): {
 1747                    if (memcmp(rhs.elems+8,"subpages",8)==0) { value_SetEnum(parent,command_FieldId_exclude_subpages); ret = true; break; }
 1748                    break;
 1749                }
 1750                case LE_STR8('u','p','d','a','t','e','_','c'): {
 1751                    if (memcmp(rhs.elems+8,"opyright",8)==0) { value_SetEnum(parent,command_FieldId_update_copyright); ret = true; break; }
 1752                    break;
 1753                }
 1754                case LE_STR8('w','i','k','i','_','c','l','e'): {
 1755                    if (memcmp(rhs.elems+8,"an_start",8)==0) { value_SetEnum(parent,command_FieldId_wiki_clean_start); ret = true; break; }
 1756                    break;
 1757                }
 1758                case LE_STR8('w','i','k','i','_','u','p','d'): {
 1759                    if (memcmp(rhs.elems+8,"ate_help",8)==0) { value_SetEnum(parent,command_FieldId_wiki_update_help); ret = true; break; }
 1760                    break;
 1761                }
 1762            }
 1763            break;
 1764        }
 1765        case 17: {
 1766            switch (algo::ReadLE64(rhs.elems)) {
 1767                case LE_STR8('i','g','n','o','r','e','_','s'): {
 1768                    if (memcmp(rhs.elems+8,"aved_edit",9)==0) { value_SetEnum(parent,command_FieldId_ignore_saved_edit); ret = true; break; }
 1769                    break;
 1770                }
 1771            }
 1772            break;
 1773        }
 1774        case 18: {
 1775            switch (algo::ReadLE64(rhs.elems)) {
 1776                case LE_STR8('w','i','k','i','_','u','p','d'): {
 1777                    if (memcmp(rhs.elems+8,"ate_fields",10)==0) { value_SetEnum(parent,command_FieldId_wiki_update_fields); ret = true; break; }
 1778                    break;
 1779                }
 1780            }
 1781            break;
 1782        }
 1783        case 21: {
 1784            switch (algo::ReadLE64(rhs.elems)) {
 1785                case LE_STR8('b','u','i','l','d','_','f','a'): {
 1786                    if (memcmp(rhs.elems+8,"il_on_warning",13)==0) { value_SetEnum(parent,command_FieldId_build_fail_on_warning); ret = true; break; }
 1787                    break;
 1788                }
 1789            }
 1790            break;
 1791        }
 1792        case 24: {
 1793            switch (algo::ReadLE64(rhs.elems)) {
 1794                case LE_STR8('s','h','o','w','_','g','i','t'): {
 1795                    if (memcmp(rhs.elems+8,"lab_system_notes",16)==0) { value_SetEnum(parent,command_FieldId_show_gitlab_system_notes); ret = true; break; }
 1796                    break;
 1797                }
 1798            }
 1799            break;
 1800        }
 1801    }
 1802    return ret;
 1803}
 1804
 1805// --- command.FieldId.value.SetStrptr
 1806// Convert string to field.
 1807// If the string is invalid, set numeric value to DFLT
 1808void command::value_SetStrptr(command::FieldId& parent, algo::strptr rhs, command_FieldIdEnum dflt) {
 1809    if (!value_SetStrptrMaybe(parent,rhs)) value_SetEnum(parent,dflt);
 1810}
 1811
 1812// --- command.FieldId.value.ReadStrptrMaybe
 1813// Convert string to field. Return success value
 1814bool command::value_ReadStrptrMaybe(command::FieldId& parent, algo::strptr rhs) {
 1815    bool retval = false;
 1816    retval = value_SetStrptrMaybe(parent,rhs); // try symbol conversion
 1817    if (!retval) { // didn't work? try reading as underlying type
 1818        retval = i32_ReadStrptrMaybe(parent.value,rhs);
 1819    }
 1820    return retval;
 1821}
 1822
 1823// --- command.FieldId..ReadStrptrMaybe
 1824// Read fields of command::FieldId from an ascii string.
 1825// The format of the string is the format of the command::FieldId's only field
 1826bool command::FieldId_ReadStrptrMaybe(command::FieldId &parent, algo::strptr in_str) {
 1827    bool retval = true;
 1828    retval = retval && value_ReadStrptrMaybe(parent, in_str);
 1829    return retval;
 1830}
 1831
 1832// --- command.FieldId..Print
 1833// print string representation of ROW to string STR
 1834// cfmt:command.FieldId.String  printfmt:Raw
 1835void command::FieldId_Print(command::FieldId& row, algo::cstring& str) {
 1836    command::value_Print(row, str);
 1837}
 1838
 1839// --- command.abt.target.Print
 1840// Print back to string
 1841void command::target_Print(command::abt& parent, algo::cstring &out) {
 1842    Regx_Print(parent.target, out);
 1843}
 1844
 1845// --- command.abt.target.ReadStrptrMaybe
 1846// Read Regx from string
 1847// Convert string to field. Return success value
 1848bool command::target_ReadStrptrMaybe(command::abt& parent, algo::strptr in) {
 1849    bool retval = true;
 1850    Regx_ReadSql(parent.target, in, true);
 1851    return retval;
 1852}
 1853
 1854// --- command.abt.disas.Print
 1855// Print back to string
 1856void command::disas_Print(command::abt& parent, algo::cstring &out) {
 1857    Regx_Print(parent.disas, out);
 1858}
 1859
 1860// --- command.abt.disas.ReadStrptrMaybe
 1861// Read Regx from string
 1862// Convert string to field. Return success value
 1863bool command::disas_ReadStrptrMaybe(command::abt& parent, algo::strptr in) {
 1864    bool retval = true;
 1865    Regx_ReadSql(parent.disas, in, false);
 1866    return retval;
 1867}
 1868
 1869// --- command.abt.cache.ToCstr
 1870// Convert numeric value of field to one of predefined string constants.
 1871// If string is found, return a static C string. Otherwise, return NULL.
 1872const char* command::cache_ToCstr(const command::abt& parent) {
 1873    const char *ret = NULL;
 1874    switch(cache_GetEnum(parent)) {
 1875        case command_abt_cache_auto        : ret = "auto";  break;
 1876        case command_abt_cache_none        : ret = "none";  break;
 1877        case command_abt_cache_gcache      : ret = "gcache";  break;
 1878        case command_abt_cache_gcache_force: ret = "gcache-force";  break;
 1879        case command_abt_cache_ccache      : ret = "ccache";  break;
 1880    }
 1881    return ret;
 1882}
 1883
 1884// --- command.abt.cache.Print
 1885// Convert cache to a string. First, attempt conversion to a known string.
 1886// If no string matches, print cache as a numeric value.
 1887void command::cache_Print(const command::abt& parent, algo::cstring &lhs) {
 1888    const char *strval = cache_ToCstr(parent);
 1889    if (strval) {
 1890        lhs << strval;
 1891    } else {
 1892        lhs << parent.cache;
 1893    }
 1894}
 1895
 1896// --- command.abt.cache.SetStrptrMaybe
 1897// Convert string to field.
 1898// If the string is invalid, do not modify field and return false.
 1899// In case of success, return true
 1900bool command::cache_SetStrptrMaybe(command::abt& parent, algo::strptr rhs) {
 1901    bool ret = false;
 1902    switch (elems_N(rhs)) {
 1903        case 4: {
 1904            switch (u64(algo::ReadLE32(rhs.elems))) {
 1905                case LE_STR4('a','u','t','o'): {
 1906                    cache_SetEnum(parent,command_abt_cache_auto); ret = true; break;
 1907                }
 1908                case LE_STR4('n','o','n','e'): {
 1909                    cache_SetEnum(parent,command_abt_cache_none); ret = true; break;
 1910                }
 1911            }
 1912            break;
 1913        }
 1914        case 6: {
 1915            switch (u64(algo::ReadLE32(rhs.elems))|(u64(algo::ReadLE16(rhs.elems+4))<<32)) {
 1916                case LE_STR6('c','c','a','c','h','e'): {
 1917                    cache_SetEnum(parent,command_abt_cache_ccache); ret = true; break;
 1918                }
 1919                case LE_STR6('g','c','a','c','h','e'): {
 1920                    cache_SetEnum(parent,command_abt_cache_gcache); ret = true; break;
 1921                }
 1922            }
 1923            break;
 1924        }
 1925        case 12: {
 1926            switch (algo::ReadLE64(rhs.elems)) {
 1927                case LE_STR8('g','c','a','c','h','e','-','f'): {
 1928                    if (memcmp(rhs.elems+8,"orce",4)==0) { cache_SetEnum(parent,command_abt_cache_gcache_force); ret = true; break; }
 1929                    break;
 1930                }
 1931            }
 1932            break;
 1933        }
 1934    }
 1935    return ret;
 1936}
 1937
 1938// --- command.abt.cache.SetStrptr
 1939// Convert string to field.
 1940// If the string is invalid, set numeric value to DFLT
 1941void command::cache_SetStrptr(command::abt& parent, algo::strptr rhs, command_abt_cache_Enum dflt) {
 1942    if (!cache_SetStrptrMaybe(parent,rhs)) cache_SetEnum(parent,dflt);
 1943}
 1944
 1945// --- command.abt.cache.ReadStrptrMaybe
 1946// Convert string to field. Return success value
 1947bool command::cache_ReadStrptrMaybe(command::abt& parent, algo::strptr rhs) {
 1948    bool retval = false;
 1949    retval = cache_SetStrptrMaybe(parent,rhs); // try symbol conversion
 1950    if (!retval) { // didn't work? try reading as underlying type
 1951        retval = u8_ReadStrptrMaybe(parent.cache,rhs);
 1952    }
 1953    return retval;
 1954}
 1955
 1956// --- command.abt..ReadFieldMaybe
 1957bool command::abt_ReadFieldMaybe(command::abt& parent, algo::strptr field, algo::strptr strval) {
 1958    bool retval = true;
 1959    command::FieldId field_id;
 1960    (void)value_SetStrptrMaybe(field_id,field);
 1961    switch(field_id) {
 1962        case command_FieldId_target: {
 1963            retval = target_ReadStrptrMaybe(parent, strval);
 1964            break;
 1965        }
 1966        case command_FieldId_in: {
 1967            retval = algo::cstring_ReadStrptrMaybe(parent.in, strval);
 1968            break;
 1969        }
 1970        case command_FieldId_out_dir: {
 1971            retval = algo::cstring_ReadStrptrMaybe(parent.out_dir, strval);
 1972            break;
 1973        }
 1974        case command_FieldId_cfg: {
 1975            retval = algo::Smallstr50_ReadStrptrMaybe(parent.cfg, strval);
 1976            break;
 1977        }
 1978        case command_FieldId_compiler: {
 1979            retval = algo::Smallstr50_ReadStrptrMaybe(parent.compiler, strval);
 1980            break;
 1981        }
 1982        case command_FieldId_uname: {
 1983            retval = algo::Smallstr50_ReadStrptrMaybe(parent.uname, strval);
 1984            break;
 1985        }
 1986        case command_FieldId_arch: {
 1987            retval = algo::Smallstr50_ReadStrptrMaybe(parent.arch, strval);
 1988            break;
 1989        }
 1990        case command_FieldId_ood: {
 1991            retval = bool_ReadStrptrMaybe(parent.ood, strval);
 1992            break;
 1993        }
 1994        case command_FieldId_list: {
 1995            retval = bool_ReadStrptrMaybe(parent.list, strval);
 1996            break;
 1997        }
 1998        case command_FieldId_listincl: {
 1999            retval = bool_ReadStrptrMaybe(parent.listincl, strval);
 2000            break;
 2001        }
 2002        case command_FieldId_build: {
 2003            retval = bool_ReadStrptrMaybe(parent.build, strval);
 2004            break;
 2005        }
 2006        case command_FieldId_preproc: {
 2007            retval = bool_ReadStrptrMaybe(parent.preproc, strval);
 2008            break;
 2009        }
 2010        case command_FieldId_clean: {
 2011            retval = bool_ReadStrptrMaybe(parent.clean, strval);
 2012            break;
 2013        }
 2014        case command_FieldId_dry_run: {
 2015            retval = bool_ReadStrptrMaybe(parent.dry_run, strval);
 2016            break;
 2017        }
 2018        case command_FieldId_maxjobs: {
 2019            retval = i32_ReadStrptrMaybe(parent.maxjobs, strval);
 2020            break;
 2021        }
 2022        case command_FieldId_printcmd: {
 2023            retval = bool_ReadStrptrMaybe(parent.printcmd, strval);
 2024            break;
 2025        }
 2026        case command_FieldId_force: {
 2027            retval = bool_ReadStrptrMaybe(parent.force, strval);
 2028            break;
 2029        }
 2030        case command_FieldId_install: {
 2031            retval = bool_ReadStrptrMaybe(parent.install, strval);
 2032            break;
 2033        }
 2034        case command_FieldId_coverity: {
 2035            retval = bool_ReadStrptrMaybe(parent.coverity, strval);
 2036            break;
 2037        }
 2038        case command_FieldId_package: {
 2039            retval = algo::cstring_ReadStrptrMaybe(parent.package, strval);
 2040            break;
 2041        }
 2042        case command_FieldId_maxerr: {
 2043            retval = u32_ReadStrptrMaybe(parent.maxerr, strval);
 2044            break;
 2045        }
 2046        case command_FieldId_disas: {
 2047            retval = disas_ReadStrptrMaybe(parent, strval);
 2048            break;
 2049        }
 2050        case command_FieldId_report: {
 2051            retval = bool_ReadStrptrMaybe(parent.report, strval);
 2052            break;
 2053        }
 2054        case command_FieldId_jcdb: {
 2055            retval = algo::cstring_ReadStrptrMaybe(parent.jcdb, strval);
 2056            break;
 2057        }
 2058        case command_FieldId_cache: {
 2059            retval = cache_ReadStrptrMaybe(parent, strval);
 2060            break;
 2061        }
 2062        default: break;
 2063    }
 2064    if (!retval) {
 2065        algo_lib::AppendErrtext("attr",field);
 2066    }
 2067    return retval;
 2068}
 2069
 2070// --- command.abt..ReadTupleMaybe
 2071// Read fields of command::abt from attributes of ascii tuple TUPLE
 2072bool command::abt_ReadTupleMaybe(command::abt &parent, algo::Tuple &tuple) {
 2073    bool retval = true;
 2074    int anon_idx = 0;
 2075    ind_beg(algo::Tuple_attrs_curs,attr,tuple) {
 2076        if (ch_N(attr.name) == 0) {
 2077            attr.name = abt_GetAnon(parent, anon_idx++);
 2078        }
 2079        retval = abt_ReadFieldMaybe(parent, attr.name, attr.value);
 2080        if (!retval) {
 2081            break;
 2082        }
 2083    }ind_end;
 2084    return retval;
 2085}
 2086
 2087// --- command.abt..Init
 2088// Set all fields to initial values.
 2089void command::abt_Init(command::abt& parent) {
 2090    Regx_ReadSql(parent.target, "", true);
 2091    parent.in = algo::strptr("data");
 2092    parent.out_dir = algo::strptr("");
 2093    parent.cfg = algo::strptr("");
 2094    parent.compiler = algo::strptr("");
 2095    parent.uname = algo::strptr("");
 2096    parent.arch = algo::strptr("");
 2097    parent.ood = bool(false);
 2098    parent.list = bool(false);
 2099    parent.listincl = bool(false);
 2100    parent.build = bool(false);
 2101    parent.preproc = bool(false);
 2102    parent.clean = bool(false);
 2103    parent.dry_run = bool(false);
 2104    parent.maxjobs = i32(0);
 2105    parent.printcmd = bool(false);
 2106    parent.force = bool(false);
 2107    parent.install = bool(false);
 2108    parent.coverity = bool(false);
 2109    parent.package = algo::strptr("");
 2110    parent.maxerr = u32(100);
 2111    Regx_ReadSql(parent.disas, "", true);
 2112    parent.report = bool(true);
 2113    parent.jcdb = algo::strptr("");
 2114    parent.cache = u8(0);
 2115}
 2116
 2117// --- command.abt..ToCmdline
 2118// Convenience function that returns a full command line
 2119// Assume command is in a directory called bin
 2120tempstr command::abt_ToCmdline(command::abt& row) {
 2121    tempstr ret;
 2122    ret << "bin/abt ";
 2123    abt_PrintArgv(row, ret);
 2124    // inherit less intense verbose, debug options
 2125    for (int i = 1; i < algo_lib::_db.cmdline.verbose; i++) {
 2126        ret << " -verbose";
 2127    }
 2128    for (int i = 1; i < algo_lib::_db.cmdline.debug; i++) {
 2129        ret << " -debug";
 2130    }
 2131    return ret;
 2132}
 2133
 2134// --- command.abt..PrintArgv
 2135// print string representation of ROW to string STR
 2136// cfmt:command.abt.Argv  printfmt:Auto
 2137void command::abt_PrintArgv(command::abt& row, algo::cstring& str) {
 2138    algo::tempstr temp;
 2139    (void)temp;
 2140    (void)str;
 2141    ch_RemoveAll(temp);
 2142    command::target_Print(const_cast<command::abt&>(row), temp);
 2143    str << " -target:";
 2144    strptr_PrintBash(temp,str);
 2145    if (!(row.in == "data")) {
 2146        ch_RemoveAll(temp);
 2147        cstring_Print(row.in, temp);
 2148        str << " -in:";
 2149        strptr_PrintBash(temp,str);
 2150    }
 2151    if (!(row.out_dir == "")) {
 2152        ch_RemoveAll(temp);
 2153        cstring_Print(row.out_dir, temp);
 2154        str << " -out_dir:";
 2155        strptr_PrintBash(temp,str);
 2156    }
 2157    if (!(row.cfg == "")) {
 2158        ch_RemoveAll(temp);
 2159        Smallstr50_Print(row.cfg, temp);
 2160        str << " -cfg:";
 2161        strptr_PrintBash(temp,str);
 2162    }
 2163    if (!(row.compiler == "")) {
 2164        ch_RemoveAll(temp);
 2165        Smallstr50_Print(row.compiler, temp);
 2166        str << " -compiler:";
 2167        strptr_PrintBash(temp,str);
 2168    }
 2169    if (!(row.uname == "")) {
 2170        ch_RemoveAll(temp);
 2171        Smallstr50_Print(row.uname, temp);
 2172        str << " -uname:";
 2173        strptr_PrintBash(temp,str);
 2174    }
 2175    if (!(row.arch == "")) {
 2176        ch_RemoveAll(temp);
 2177        Smallstr50_Print(row.arch, temp);
 2178        str << " -arch:";
 2179        strptr_PrintBash(temp,str);
 2180    }
 2181    if (!(row.ood == false)) {
 2182        ch_RemoveAll(temp);
 2183        bool_Print(row.ood, temp);
 2184        str << " -ood:";
 2185        strptr_PrintBash(temp,str);
 2186    }
 2187    if (!(row.list == false)) {
 2188        ch_RemoveAll(temp);
 2189        bool_Print(row.list, temp);
 2190        str << " -list:";
 2191        strptr_PrintBash(temp,str);
 2192    }
 2193    if (!(row.listincl == false)) {
 2194        ch_RemoveAll(temp);
 2195        bool_Print(row.listincl, temp);
 2196        str << " -listincl:";
 2197        strptr_PrintBash(temp,str);
 2198    }
 2199    if (!(row.build == false)) {
 2200        ch_RemoveAll(temp);
 2201        bool_Print(row.build, temp);
 2202        str << " -build:";
 2203        strptr_PrintBash(temp,str);
 2204    }
 2205    if (!(row.preproc == false)) {
 2206        ch_RemoveAll(temp);
 2207        bool_Print(row.preproc, temp);
 2208        str << " -preproc:";
 2209        strptr_PrintBash(temp,str);
 2210    }
 2211    if (!(row.clean == false)) {
 2212        ch_RemoveAll(temp);
 2213        bool_Print(row.clean, temp);
 2214        str << " -clean:";
 2215        strptr_PrintBash(temp,str);
 2216    }
 2217    if (!(row.dry_run == false)) {
 2218        ch_RemoveAll(temp);
 2219        bool_Print(row.dry_run, temp);
 2220        str << " -dry_run:";
 2221        strptr_PrintBash(temp,str);
 2222    }
 2223    if (!(row.maxjobs == 0)) {
 2224        ch_RemoveAll(temp);
 2225        i32_Print(row.maxjobs, temp);
 2226        str << " -maxjobs:";
 2227        strptr_PrintBash(temp,str);
 2228    }
 2229    if (!(row.printcmd == false)) {
 2230        ch_RemoveAll(temp);
 2231        bool_Print(row.printcmd, temp);
 2232        str << " -printcmd:";
 2233        strptr_PrintBash(temp,str);
 2234    }
 2235    if (!(row.force == false)) {
 2236        ch_RemoveAll(temp);
 2237        bool_Print(row.force, temp);
 2238        str << " -force:";
 2239        strptr_PrintBash(temp,str);
 2240    }
 2241    if (!(row.install == false)) {
 2242        ch_RemoveAll(temp);
 2243        bool_Print(row.install, temp);
 2244        str << " -install:";
 2245        strptr_PrintBash(temp,str);
 2246    }
 2247    if (!(row.coverity == false)) {
 2248        ch_RemoveAll(temp);
 2249        bool_Print(row.coverity, temp);
 2250        str << " -coverity:";
 2251        strptr_PrintBash(temp,str);
 2252    }
 2253    if (!(row.package == "")) {
 2254        ch_RemoveAll(temp);
 2255        cstring_Print(row.package, temp);
 2256        str << " -package:";
 2257        strptr_PrintBash(temp,str);
 2258    }
 2259    if (!(row.maxerr == 100)) {
 2260        ch_RemoveAll(temp);
 2261        u32_Print(row.maxerr, temp);
 2262        str << " -maxerr:";
 2263        strptr_PrintBash(temp,str);
 2264    }
 2265    if (!(row.disas.expr == "")) {
 2266        ch_RemoveAll(temp);
 2267        command::disas_Print(const_cast<command::abt&>(row), temp);
 2268        str << " -disas:";
 2269        strptr_PrintBash(temp,str);
 2270    }
 2271    if (!(row.report == true)) {
 2272        ch_RemoveAll(temp);
 2273        bool_Print(row.report, temp);
 2274        str << " -report:";
 2275        strptr_PrintBash(temp,str);
 2276    }
 2277    if (!(row.jcdb == "")) {
 2278        ch_RemoveAll(temp);
 2279        cstring_Print(row.jcdb, temp);
 2280        str << " -jcdb:";
 2281        strptr_PrintBash(temp,str);
 2282    }
 2283    if (!(row.cache == 0)) {
 2284        ch_RemoveAll(temp);
 2285        command::cache_Print(const_cast<command::abt&>(row), temp);
 2286        str << " -cache:";
 2287        strptr_PrintBash(temp,str);
 2288    }
 2289}
 2290
 2291// --- command.abt..GetAnon
 2292algo::strptr command::abt_GetAnon(command::abt &parent, i32 idx) {
 2293    (void)parent;//only to avoid -Wunused-parameter
 2294    switch(idx) {
 2295        case(0): return strptr("target", 6);
 2296        default: return algo::strptr();
 2297    }
 2298}
 2299
 2300// --- command.abt..NArgs
 2301// Used with command lines
 2302// Return # of command-line arguments that must follow this argument
 2303// If FIELD is invalid, return -1
 2304i32 command::abt_NArgs(command::FieldId field, algo::strptr& out_dflt, bool* out_anon) {
 2305    i32 retval = 1;
 2306    switch (field) {
 2307        case command_FieldId_target: { // $comment
 2308            *out_anon = true;
 2309        } break;
 2310        case command_FieldId_in: { // $comment
 2311            *out_anon = false;
 2312        } break;
 2313        case command_FieldId_out_dir: { // $comment
 2314            *out_anon = false;
 2315        } break;
 2316        case command_FieldId_cfg: { // $comment
 2317            *out_anon = false;
 2318        } break;
 2319        case command_FieldId_compiler: { // $comment
 2320            *out_anon = false;
 2321        } break;
 2322        case command_FieldId_uname: { // $comment
 2323            *out_anon = false;
 2324        } break;
 2325        case command_FieldId_arch: { // $comment
 2326            *out_anon = false;
 2327        } break;
 2328        case command_FieldId_ood: { // $comment
 2329            *out_anon = false;
 2330            retval=0;
 2331            out_dflt="Y";
 2332        } break;
 2333        case command_FieldId_list: { // bool: no argument required but value may be specified as ood:Y
 2334            *out_anon = false;
 2335            retval=0;
 2336            out_dflt="Y";
 2337        } break;
 2338        case command_FieldId_listincl: { // bool: no argument required but value may be specified as list:Y
 2339            *out_anon = false;
 2340            retval=0;
 2341            out_dflt="Y";
 2342        } break;
 2343        case command_FieldId_build: { // bool: no argument required but value may be specified as listincl:Y
 2344            *out_anon = false;
 2345            retval=0;
 2346            out_dflt="Y";
 2347        } break;
 2348        case command_FieldId_preproc: { // bool: no argument required but value may be specified as build:Y
 2349            *out_anon = false;
 2350            retval=0;
 2351            out_dflt="Y";
 2352        } break;
 2353        case command_FieldId_clean: { // bool: no argument required but value may be specified as preproc:Y
 2354            *out_anon = false;
 2355            retval=0;
 2356            out_dflt="Y";
 2357        } break;
 2358        case command_FieldId_dry_run: { // bool: no argument required but value may be specified as clean:Y
 2359            *out_anon = false;
 2360            retval=0;
 2361            out_dflt="Y";
 2362        } break;
 2363        case command_FieldId_maxjobs: { // bool: no argument required but value may be specified as dry_run:Y
 2364            *out_anon = false;
 2365        } break;
 2366        case command_FieldId_printcmd: { // bool: no argument required but value may be specified as dry_run:Y
 2367            *out_anon = false;
 2368            retval=0;
 2369            out_dflt="Y";
 2370        } break;
 2371        case command_FieldId_force: { // bool: no argument required but value may be specified as printcmd:Y
 2372            *out_anon = false;
 2373            retval=0;
 2374            out_dflt="Y";
 2375        } break;
 2376        case command_FieldId_install: { // bool: no argument required but value may be specified as force:Y
 2377            *out_anon = false;
 2378            retval=0;
 2379            out_dflt="Y";
 2380        } break;
 2381        case command_FieldId_coverity: { // bool: no argument required but value may be specified as install:Y
 2382            *out_anon = false;
 2383            retval=0;
 2384            out_dflt="Y";
 2385        } break;
 2386        case command_FieldId_package: { // bool: no argument required but value may be specified as coverity:Y
 2387            *out_anon = false;
 2388        } break;
 2389        case command_FieldId_maxerr: { // bool: no argument required but value may be specified as coverity:Y
 2390            *out_anon = false;
 2391        } break;
 2392        case command_FieldId_disas: { // bool: no argument required but value may be specified as coverity:Y
 2393            *out_anon = false;
 2394        } break;
 2395        case command_FieldId_report: { // bool: no argument required but value may be specified as coverity:Y
 2396            *out_anon = false;
 2397            retval=0;
 2398            out_dflt="Y";
 2399        } break;
 2400        case command_FieldId_jcdb: { // bool: no argument required but value may be specified as report:Y
 2401            *out_anon = false;
 2402        } break;
 2403        case command_FieldId_cache: { // bool: no argument required but value may be specified as report:Y
 2404            *out_anon = false;
 2405        } break;
 2406        default:
 2407        retval=-1; // unrecognized
 2408    }
 2409    return retval;
 2410}
 2411
 2412// --- command.abt_md.readme.Print
 2413// Print back to string
 2414void command::readme_Print(command::abt_md& parent, algo::cstring &out) {
 2415    Regx_Print(parent.readme, out);
 2416}
 2417
 2418// --- command.abt_md.readme.ReadStrptrMaybe
 2419// Read Regx from string
 2420// Convert string to field. Return success value
 2421bool command::readme_ReadStrptrMaybe(command::abt_md& parent, algo::strptr in) {
 2422    bool retval = true;
 2423    Regx_ReadSql(parent.readme, in, true);
 2424    return retval;
 2425}
 2426
 2427// --- command.abt_md.ns.Print
 2428// Print back to string
 2429void command::ns_Print(command::abt_md& parent, algo::cstring &out) {
 2430    Regx_Print(parent.ns, out);
 2431}
 2432
 2433// --- command.abt_md.ns.ReadStrptrMaybe
 2434// Read Regx from string
 2435// Convert string to field. Return success value
 2436bool command::ns_ReadStrptrMaybe(command::abt_md& parent, algo::strptr in) {
 2437    bool retval = true;
 2438    Regx_ReadSql(parent.ns, in, true);
 2439    return retval;
 2440}
 2441
 2442// --- command.abt_md.section.Print
 2443// Print back to string
 2444void command::section_Print(command::abt_md& parent, algo::cstring &out) {
 2445    Regx_Print(parent.section, out);
 2446}
 2447
 2448// --- command.abt_md.section.ReadStrptrMaybe
 2449// Read Regx from string
 2450// Convert string to field. Return success value
 2451bool command::section_ReadStrptrMaybe(command::abt_md& parent, algo::strptr in) {
 2452    bool retval = true;
 2453    Regx_ReadSql(parent.section, in, true);
 2454    return retval;
 2455}
 2456
 2457// --- command.abt_md..ReadFieldMaybe
 2458bool command::abt_md_ReadFieldMaybe(command::abt_md& parent, algo::strptr field, algo::strptr strval) {
 2459    bool retval = true;
 2460    command::FieldId field_id;
 2461    (void)value_SetStrptrMaybe(field_id,field);
 2462    switch(field_id) {
 2463        case command_FieldId_in: {
 2464            retval = algo::cstring_ReadStrptrMaybe(parent.in, strval);
 2465            break;
 2466        }
 2467        case command_FieldId_readme: {
 2468            retval = readme_ReadStrptrMaybe(parent, strval);
 2469            break;
 2470        }
 2471        case command_FieldId_ns: {
 2472            retval = ns_ReadStrptrMaybe(parent, strval);
 2473            break;
 2474        }
 2475        case command_FieldId_section: {
 2476            retval = section_ReadStrptrMaybe(parent, strval);
 2477            break;
 2478        }
 2479        case command_FieldId_update: {
 2480            retval = bool_ReadStrptrMaybe(parent.update, strval);
 2481            break;
 2482        }
 2483        case command_FieldId_check: {
 2484            retval = bool_ReadStrptrMaybe(parent.check, strval);
 2485            break;
 2486        }
 2487        case command_FieldId_link: {
 2488            retval = bool_ReadStrptrMaybe(parent.link, strval);
 2489            break;
 2490        }
 2491        case command_FieldId_anchor: {
 2492            retval = bool_ReadStrptrMaybe(parent.anchor, strval);
 2493            break;
 2494        }
 2495        case command_FieldId_print: {
 2496            retval = bool_ReadStrptrMaybe(parent.print, strval);
 2497            break;
 2498        }
 2499        case command_FieldId_dry_run: {
 2500            retval = bool_ReadStrptrMaybe(parent.dry_run, strval);
 2501            break;
 2502        }
 2503        default: break;
 2504    }
 2505    if (!retval) {
 2506        algo_lib::AppendErrtext("attr",field);
 2507    }
 2508    return retval;
 2509}
 2510
 2511// --- command.abt_md..ReadTupleMaybe
 2512// Read fields of command::abt_md from attributes of ascii tuple TUPLE
 2513bool command::abt_md_ReadTupleMaybe(command::abt_md &parent, algo::Tuple &tuple) {
 2514    bool retval = true;
 2515    int anon_idx = 0;
 2516    ind_beg(algo::Tuple_attrs_curs,attr,tuple) {
 2517        if (ch_N(attr.name) == 0) {
 2518            attr.name = abt_md_GetAnon(parent, anon_idx++);
 2519        }
 2520        retval = abt_md_ReadFieldMaybe(parent, attr.name, attr.value);
 2521        if (!retval) {
 2522            break;
 2523        }
 2524    }ind_end;
 2525    return retval;
 2526}
 2527
 2528// --- command.abt_md..Init
 2529// Set all fields to initial values.
 2530void command::abt_md_Init(command::abt_md& parent) {
 2531    parent.in = algo::strptr("data");
 2532    Regx_ReadSql(parent.readme, "%", true);
 2533    Regx_ReadSql(parent.ns, "", true);
 2534    Regx_ReadSql(parent.section, "%", true);
 2535    parent.update = bool(true);
 2536    parent.check = bool(false);
 2537    parent.link = bool(false);
 2538    parent.anchor = bool(false);
 2539    parent.print = bool(false);
 2540    parent.dry_run = bool(false);
 2541}
 2542
 2543// --- command.abt_md..ToCmdline
 2544// Convenience function that returns a full command line
 2545// Assume command is in a directory called bin
 2546tempstr command::abt_md_ToCmdline(command::abt_md& row) {
 2547    tempstr ret;
 2548    ret << "bin/abt_md ";
 2549    abt_md_PrintArgv(row, ret);
 2550    // inherit less intense verbose, debug options
 2551    for (int i = 1; i < algo_lib::_db.cmdline.verbose; i++) {
 2552        ret << " -verbose";
 2553    }
 2554    for (int i = 1; i < algo_lib::_db.cmdline.debug; i++) {
 2555        ret << " -debug";
 2556    }
 2557    return ret;
 2558}
 2559
 2560// --- command.abt_md..PrintArgv
 2561// print string representation of ROW to string STR
 2562// cfmt:command.abt_md.Argv  printfmt:Tuple
 2563void command::abt_md_PrintArgv(command::abt_md& row, algo::cstring& str) {
 2564    algo::tempstr temp;
 2565    (void)temp;
 2566    (void)str;
 2567    if (!(row.in == "data")) {
 2568        ch_RemoveAll(temp);
 2569        cstring_Print(row.in, temp);
 2570        str << " -in:";
 2571        strptr_PrintBash(temp,str);
 2572    }
 2573    ch_RemoveAll(temp);
 2574    command::readme_Print(const_cast<command::abt_md&>(row), temp);
 2575    str << " -readme:";
 2576    strptr_PrintBash(temp,str);
 2577    if (!(row.ns.expr == "")) {
 2578        ch_RemoveAll(temp);
 2579        command::ns_Print(const_cast<command::abt_md&>(row), temp);
 2580        str << " -ns:";
 2581        strptr_PrintBash(temp,str);
 2582    }
 2583    ch_RemoveAll(temp);
 2584    command::section_Print(const_cast<command::abt_md&>(row), temp);
 2585    str << " -section:";
 2586    strptr_PrintBash(temp,str);
 2587    if (!(row.update == true)) {
 2588        ch_RemoveAll(temp);
 2589        bool_Print(row.update, temp);
 2590        str << " -update:";
 2591        strptr_PrintBash(temp,str);
 2592    }
 2593    if (!(row.check == false)) {
 2594        ch_RemoveAll(temp);
 2595        bool_Print(row.check, temp);
 2596        str << " -check:";
 2597        strptr_PrintBash(temp,str);
 2598    }
 2599    if (!(row.link == false)) {
 2600        ch_RemoveAll(temp);
 2601        bool_Print(row.link, temp);
 2602        str << " -link:";
 2603        strptr_PrintBash(temp,str);
 2604    }
 2605    if (!(row.anchor == false)) {
 2606        ch_RemoveAll(temp);
 2607        bool_Print(row.anchor, temp);
 2608        str << " -anchor:";
 2609        strptr_PrintBash(temp,str);
 2610    }
 2611    if (!(row.print == false)) {
 2612        ch_RemoveAll(temp);
 2613        bool_Print(row.print, temp);
 2614        str << " -print:";
 2615        strptr_PrintBash(temp,str);
 2616    }
 2617    if (!(row.dry_run == false)) {
 2618        ch_RemoveAll(temp);
 2619        bool_Print(row.dry_run, temp);
 2620        str << " -dry_run:";
 2621        strptr_PrintBash(temp,str);
 2622    }
 2623}
 2624
 2625// --- command.abt_md..GetAnon
 2626algo::strptr command::abt_md_GetAnon(command::abt_md &parent, i32 idx) {
 2627    (void)parent;//only to avoid -Wunused-parameter
 2628    switch(idx) {
 2629        case(0): return strptr("readme", 6);
 2630        case(1): return strptr("section", 7);
 2631        default: return algo::strptr();
 2632    }
 2633}
 2634
 2635// --- command.abt_md..NArgs
 2636// Used with command lines
 2637// Return # of command-line arguments that must follow this argument
 2638// If FIELD is invalid, return -1
 2639i32 command::abt_md_NArgs(command::FieldId field, algo::strptr& out_dflt, bool* out_anon) {
 2640    i32 retval = 1;
 2641    switch (field) {
 2642        case command_FieldId_in: { // $comment
 2643            *out_anon = false;
 2644        } break;
 2645        case command_FieldId_readme: { // $comment
 2646            *out_anon = true;
 2647        } break;
 2648        case command_FieldId_ns: { // $comment
 2649            *out_anon = false;
 2650        } break;
 2651        case command_FieldId_section: { // $comment
 2652            *out_anon = true;
 2653        } break;
 2654        case command_FieldId_update: { // $comment
 2655            *out_anon = false;
 2656            retval=0;
 2657            out_dflt="Y";
 2658        } break;
 2659        case command_FieldId_check: { // bool: no argument required but value may be specified as update:Y
 2660            *out_anon = false;
 2661            retval=0;
 2662            out_dflt="Y";
 2663        } break;
 2664        case command_FieldId_link: { // bool: no argument required but value may be specified as check:Y
 2665            *out_anon = false;
 2666            retval=0;
 2667            out_dflt="Y";
 2668        } break;
 2669        case command_FieldId_anchor: { // bool: no argument required but value may be specified as link:Y
 2670            *out_anon = false;
 2671            retval=0;
 2672            out_dflt="Y";
 2673        } break;
 2674        case command_FieldId_print: { // bool: no argument required but value may be specified as anchor:Y
 2675            *out_anon = false;
 2676            retval=0;
 2677            out_dflt="Y";
 2678        } break;
 2679        case command_FieldId_dry_run: { // bool: no argument required but value may be specified as print:Y
 2680            *out_anon = false;
 2681            retval=0;
 2682            out_dflt="Y";
 2683        } break;
 2684        default:
 2685        retval=-1; // unrecognized
 2686    }
 2687    return retval;
 2688}
 2689
 2690// --- command.abt_md_proc.abt_md.Start
 2691// Start subprocess
 2692// If subprocess already running, do nothing. Otherwise, start it
 2693int command::abt_md_Start(command::abt_md_proc& parent) {
 2694    int retval = 0;
 2695    if (parent.pid == 0) {
 2696        verblog(abt_md_ToCmdline(parent)); // maybe print command
 2697#ifdef WIN32
 2698        algo_lib::ResolveExecFname(parent.path);
 2699        tempstr cmdline(abt_md_ToCmdline(parent));
 2700        parent.pid = dospawn(Zeroterm(parent.path),Zeroterm(cmdline),parent.timeout,parent.fstdin,parent.fstdout,parent.fstderr);
 2701#else
 2702        parent.pid = fork();
 2703        if (parent.pid == 0) { // child
 2704            algo_lib::DieWithParent();
 2705            if (parent.timeout > 0) {
 2706                alarm(parent.timeout);
 2707            }
 2708            if (retval==0) retval=algo_lib::ApplyRedirect(parent.fstdin , 0);
 2709            if (retval==0) retval=algo_lib::ApplyRedirect(parent.fstdout, 1);
 2710            if (retval==0) retval=algo_lib::ApplyRedirect(parent.fstderr, 2);
 2711            if (retval==0) retval= abt_md_Execv(parent);
 2712            if (retval != 0) { // if start fails, print error
 2713                int err=errno;
 2714                prerr("command.abt_md_execv"
 2715                <<Keyval("errno",err)
 2716                <<Keyval("errstr",strerror(err))
 2717                <<Keyval("comment","Execv failed"));
 2718            }
 2719            _exit(127); // if failed to start, exit anyway
 2720        } else if (parent.pid == -1) {
 2721            retval = errno; // failed to fork
 2722        }
 2723#endif
 2724    }
 2725    parent.status = parent.pid > 0 ? 0 : -1; // if didn't start, set error status
 2726    return retval;
 2727}
 2728
 2729// --- command.abt_md_proc.abt_md.StartRead
 2730// Start subprocess & Read output
 2731algo::Fildes command::abt_md_StartRead(command::abt_md_proc& parent, algo_lib::FFildes &read) {
 2732    int pipefd[2];
 2733    int rc=pipe(pipefd);
 2734    (void)rc;
 2735    read.fd.value = pipefd[0];
 2736    parent.fstdout  << ">&" << pipefd[1];
 2737    abt_md_Start(parent);
 2738    (void)close(pipefd[1]);
 2739    return read.fd;
 2740}
 2741
 2742// --- command.abt_md_proc.abt_md.Kill
 2743// Kill subprocess and wait
 2744void command::abt_md_Kill(command::abt_md_proc& parent) {
 2745    if (parent.pid != 0) {
 2746        kill(parent.pid,9);
 2747        abt_md_Wait(parent);
 2748    }
 2749}
 2750
 2751// --- command.abt_md_proc.abt_md.Wait
 2752// Wait for subprocess to return
 2753void command::abt_md_Wait(command::abt_md_proc& parent) {
 2754    if (parent.pid > 0) {
 2755        int wait_flags = 0;
 2756        int wait_status = 0;
 2757        int rc = -1;
 2758        do {
 2759            // really wait for subprocess to exit
 2760            rc = waitpid(parent.pid,&wait_status,wait_flags);
 2761        } while (rc==-1 && errno==EINTR);
 2762        if (rc == parent.pid) {
 2763            parent.status = wait_status;
 2764            parent.pid = 0;
 2765        }
 2766    }
 2767}
 2768
 2769// --- command.abt_md_proc.abt_md.Exec
 2770// Start + Wait
 2771// Execute subprocess and return exit code
 2772int command::abt_md_Exec(command::abt_md_proc& parent) {
 2773    abt_md_Start(parent);
 2774    abt_md_Wait(parent);
 2775    return parent.status;
 2776}
 2777
 2778// --- command.abt_md_proc.abt_md.ExecX
 2779// Start + Wait, throw exception on error
 2780// Execute subprocess; throw human-readable exception on error
 2781void command::abt_md_ExecX(command::abt_md_proc& parent) {
 2782    int rc = abt_md_Exec(parent);
 2783    vrfy(rc==0, tempstr() << "algo_lib.exec" << Keyval("cmd",abt_md_ToCmdline(parent))
 2784    << Keyval("comment",algo::DescribeWaitStatus(parent.status)));
 2785}
 2786
 2787// --- command.abt_md_proc.abt_md.Execv
 2788// Call execv()
 2789// Call execv with specified parameters
 2790int command::abt_md_Execv(command::abt_md_proc& parent) {
 2791    int ret = 0;
 2792    algo::StringAry args;
 2793    abt_md_ToArgv(parent, args);
 2794    char **argv = (char**)alloca((ary_N(args)+1)*sizeof(*argv));
 2795    ind_beg(algo::StringAry_ary_curs,arg,args) {
 2796        argv[ind_curs(arg).index] = Zeroterm(arg);
 2797    }ind_end;
 2798    argv[ary_N(args)] = NULL;
 2799    // if parent.path is relative, search for it in PATH
 2800    algo_lib::ResolveExecFname(parent.path);
 2801    ret = execv(Zeroterm(parent.path),argv);
 2802    return ret;
 2803}
 2804
 2805// --- command.abt_md_proc.abt_md.ToCmdline
 2806algo::tempstr command::abt_md_ToCmdline(command::abt_md_proc& parent) {
 2807    algo::tempstr retval;
 2808    retval << parent.path << " ";
 2809    command::abt_md_PrintArgv(parent.cmd,retval);
 2810    if (ch_N(parent.fstdin)) {
 2811        retval << " " << parent.fstdin;
 2812    }
 2813    if (ch_N(parent.fstdout)) {
 2814        retval << " " << parent.fstdout;
 2815    }
 2816    if (ch_N(parent.fstderr)) {
 2817        retval << " 2" << parent.fstderr;
 2818    }
 2819    return retval;
 2820}
 2821
 2822// --- command.abt_md_proc.abt_md.ToArgv
 2823// Form array from the command line
 2824void command::abt_md_ToArgv(command::abt_md_proc& parent, algo::StringAry& args) {
 2825    ary_RemoveAll(args);
 2826    ary_Alloc(args) << parent.path;
 2827
 2828    if (parent.cmd.in != "data") {
 2829        cstring *arg = &ary_Alloc(args);
 2830        *arg << "-in:";
 2831        cstring_Print(parent.cmd.in, *arg);
 2832    }
 2833
 2834    if (parent.cmd.readme.expr != "%") {
 2835        cstring *arg = &ary_Alloc(args);
 2836        *arg << "-readme:";
 2837        command::readme_Print(parent.cmd, *arg);
 2838    }
 2839
 2840    if (parent.cmd.ns.expr != "") {
 2841        cstring *arg = &ary_Alloc(args);
 2842        *arg << "-ns:";
 2843        command::ns_Print(parent.cmd, *arg);
 2844    }
 2845
 2846    if (parent.cmd.section.expr != "%") {
 2847        cstring *arg = &ary_Alloc(args);
 2848        *arg << "-section:";
 2849        command::section_Print(parent.cmd, *arg);
 2850    }
 2851
 2852    if (parent.cmd.update != true) {
 2853        cstring *arg = &ary_Alloc(args);
 2854        *arg << "-update:";
 2855        bool_Print(parent.cmd.update, *arg);
 2856    }
 2857
 2858    if (parent.cmd.check != false) {
 2859        cstring *arg = &ary_Alloc(args);
 2860        *arg << "-check:";
 2861        bool_Print(parent.cmd.check, *arg);
 2862    }
 2863
 2864    if (parent.cmd.link != false) {
 2865        cstring *arg = &ary_Alloc(args);
 2866        *arg << "-link:";
 2867        bool_Print(parent.cmd.link, *arg);
 2868    }
 2869
 2870    if (parent.cmd.anchor != false) {
 2871        cstring *arg = &ary_Alloc(args);
 2872        *arg << "-anchor:";
 2873        bool_Print(parent.cmd.anchor, *arg);
 2874    }
 2875
 2876    if (parent.cmd.print != false) {
 2877        cstring *arg = &ary_Alloc(args);
 2878        *arg << "-print:";
 2879        bool_Print(parent.cmd.print, *arg);
 2880    }
 2881
 2882    if (parent.cmd.dry_run != false) {
 2883        cstring *arg = &ary_Alloc(args);
 2884        *arg << "-dry_run:";
 2885        bool_Print(parent.cmd.dry_run, *arg);
 2886    }
 2887    for (int i=1; i < algo_lib::_db.cmdline.verbose; ++i) {
 2888        ary_Alloc(args) << "-verbose";
 2889    }
 2890}
 2891
 2892// --- command.abt_md_proc..Uninit
 2893void command::abt_md_proc_Uninit(command::abt_md_proc& parent) {
 2894    command::abt_md_proc &row = parent; (void)row;
 2895
 2896    // command.abt_md_proc.abt_md.Uninit (Exec)  //
 2897    abt_md_Kill(parent); // kill child, ensure forward progress
 2898}
 2899
 2900// --- command.abt_proc.abt.Start
 2901// Start subprocess
 2902// If subprocess already running, do nothing. Otherwise, start it
 2903int command::abt_Start(command::abt_proc& parent) {
 2904    int retval = 0;
 2905    if (parent.pid == 0) {
 2906        verblog(abt_ToCmdline(parent)); // maybe print command
 2907#ifdef WIN32
 2908        algo_lib::ResolveExecFname(parent.path);
 2909        tempstr cmdline(abt_ToCmdline(parent));
 2910        parent.pid = dospawn(Zeroterm(parent.path),Zeroterm(cmdline),parent.timeout,parent.fstdin,parent.fstdout,parent.fstderr);
 2911#else
 2912        parent.pid = fork();
 2913        if (parent.pid == 0) { // child
 2914            algo_lib::DieWithParent();
 2915            if (parent.timeout > 0) {
 2916                alarm(parent.timeout);
 2917            }
 2918            if (retval==0) retval=algo_lib::ApplyRedirect(parent.fstdin , 0);
 2919            if (retval==0) retval=algo_lib::ApplyRedirect(parent.fstdout, 1);
 2920            if (retval==0) retval=algo_lib::ApplyRedirect(parent.fstderr, 2);
 2921            if (retval==0) retval= abt_Execv(parent);
 2922            if (retval != 0) { // if start fails, print error
 2923                int err=errno;
 2924                prerr("command.abt_execv"
 2925                <<Keyval("errno",err)
 2926                <<Keyval("errstr",strerror(err))
 2927                <<Keyval("comment","Execv failed"));
 2928            }
 2929            _exit(127); // if failed to start, exit anyway
 2930        } else if (parent.pid == -1) {
 2931            retval = errno; // failed to fork
 2932        }
 2933#endif
 2934    }
 2935    parent.status = parent.pid > 0 ? 0 : -1; // if didn't start, set error status
 2936    return retval;
 2937}
 2938
 2939// --- command.abt_proc.abt.StartRead
 2940// Start subprocess & Read output
 2941algo::Fildes command::abt_StartRead(command::abt_proc& parent, algo_lib::FFildes &read) {
 2942    int pipefd[2];
 2943    int rc=pipe(pipefd);
 2944    (void)rc;
 2945    read.fd.value = pipefd[0];
 2946    parent.fstdout  << ">&" << pipefd[1];
 2947    abt_Start(parent);
 2948    (void)close(pipefd[1]);
 2949    return read.fd;
 2950}
 2951
 2952// --- command.abt_proc.abt.Kill
 2953// Kill subprocess and wait
 2954void command::abt_Kill(command::abt_proc& parent) {
 2955    if (parent.pid != 0) {
 2956        kill(parent.pid,9);
 2957        abt_Wait(parent);
 2958    }
 2959}
 2960
 2961// --- command.abt_proc.abt.Wait
 2962// Wait for subprocess to return
 2963void command::abt_Wait(command::abt_proc& parent) {
 2964    if (parent.pid > 0) {
 2965        int wait_flags = 0;
 2966        int wait_status = 0;
 2967        int rc = -1;
 2968        do {
 2969            // really wait for subprocess to exit
 2970            rc = waitpid(parent.pid,&wait_status,wait_flags);
 2971        } while (rc==-1 && errno==EINTR);
 2972        if (rc == parent.pid) {
 2973            parent.status = wait_status;
 2974            parent.pid = 0;
 2975        }
 2976    }
 2977}
 2978
 2979// --- command.abt_proc.abt.Exec
 2980// Start + Wait
 2981// Execute subprocess and return exit code
 2982int command::abt_Exec(command::abt_proc& parent) {
 2983    abt_Start(parent);
 2984    abt_Wait(parent);
 2985    return parent.status;
 2986}
 2987
 2988// --- command.abt_proc.abt.ExecX
 2989// Start + Wait, throw exception on error
 2990// Execute subprocess; throw human-readable exception on error
 2991void command::abt_ExecX(command::abt_proc& parent) {
 2992    int rc = abt_Exec(parent);
 2993    vrfy(rc==0, tempstr() << "algo_lib.exec" << Keyval("cmd",abt_ToCmdline(parent))
 2994    << Keyval("comment",algo::DescribeWaitStatus(parent.status)));
 2995}
 2996
 2997// --- command.abt_proc.abt.Execv
 2998// Call execv()
 2999// Call execv with specified parameters
 3000int command::abt_Execv(command::abt_proc& parent) {
 3001    int ret = 0;
 3002    algo::StringAry args;
 3003    abt_ToArgv(parent, args);
 3004    char **argv = (char**)alloca((ary_N(args)+1)*sizeof(*argv));
 3005    ind_beg(algo::StringAry_ary_curs,arg,args) {
 3006        argv[ind_curs(arg).index] = Zeroterm(arg);
 3007    }ind_end;
 3008    argv[ary_N(args)] = NULL;
 3009    // if parent.path is relative, search for it in PATH
 3010    algo_lib::ResolveExecFname(parent.path);
 3011    ret = execv(Zeroterm(parent.path),argv);
 3012    return ret;
 3013}
 3014
 3015// --- command.abt_proc.abt.ToCmdline
 3016algo::tempstr command::abt_ToCmdline(command::abt_proc& parent) {
 3017    algo::tempstr retval;
 3018    retval << parent.path << " ";
 3019    command::abt_PrintArgv(parent.cmd,retval);
 3020    if (ch_N(parent.fstdin)) {
 3021        retval << " " << parent.fstdin;
 3022    }
 3023    if (ch_N(parent.fstdout)) {
 3024        retval << " " << parent.fstdout;
 3025    }
 3026    if (ch_N(parent.fstderr)) {
 3027        retval << " 2" << parent.fstderr;
 3028    }
 3029    return retval;
 3030}
 3031
 3032// --- command.abt_proc.abt.ToArgv
 3033// Form array from the command line
 3034void command::abt_ToArgv(command::abt_proc& parent, algo::StringAry& args) {
 3035    ary_RemoveAll(args);
 3036    ary_Alloc(args) << parent.path;
 3037
 3038    if (parent.cmd.target.expr != "") {
 3039        cstring *arg = &ary_Alloc(args);
 3040        *arg << "-target:";
 3041        command::target_Print(parent.cmd, *arg);
 3042    }
 3043
 3044    if (parent.cmd.in != "data") {
 3045        cstring *arg = &ary_Alloc(args);
 3046        *arg << "-in:";
 3047        cstring_Print(parent.cmd.in, *arg);
 3048    }
 3049
 3050    if (parent.cmd.out_dir != "") {
 3051        cstring *arg = &ary_Alloc(args);
 3052        *arg << "-out_dir:";
 3053        cstring_Print(parent.cmd.out_dir, *arg);
 3054    }
 3055
 3056    if (parent.cmd.cfg != "") {
 3057        cstring *arg = &ary_Alloc(args);
 3058        *arg << "-cfg:";
 3059        Smallstr50_Print(parent.cmd.cfg, *arg);
 3060    }
 3061
 3062    if (parent.cmd.compiler != "") {
 3063        cstring *arg = &ary_Alloc(args);
 3064        *arg << "-compiler:";
 3065        Smallstr50_Print(parent.cmd.compiler, *arg);
 3066    }
 3067
 3068    if (parent.cmd.uname != "") {
 3069        cstring *arg = &ary_Alloc(args);
 3070        *arg << "-uname:";
 3071        Smallstr50_Print(parent.cmd.uname, *arg);
 3072    }
 3073
 3074    if (parent.cmd.arch != "") {
 3075        cstring *arg = &ary_Alloc(args);
 3076        *arg << "-arch:";
 3077        Smallstr50_Print(parent.cmd.arch, *arg);
 3078    }
 3079
 3080    if (parent.cmd.ood != false) {
 3081        cstring *arg = &ary_Alloc(args);
 3082        *arg << "-ood:";
 3083        bool_Print(parent.cmd.ood, *arg);
 3084    }
 3085
 3086    if (parent.cmd.list != false) {
 3087        cstring *arg = &ary_Alloc(args);
 3088        *arg << "-list:";
 3089        bool_Print(parent.cmd.list, *arg);
 3090    }
 3091
 3092    if (parent.cmd.listincl != false) {
 3093        cstring *arg = &ary_Alloc(args);
 3094        *arg << "-listincl:";
 3095        bool_Print(parent.cmd.listincl, *arg);
 3096    }
 3097
 3098    if (parent.cmd.build != false) {
 3099        cstring *arg = &ary_Alloc(args);
 3100        *arg << "-build:";
 3101        bool_Print(parent.cmd.build, *arg);
 3102    }
 3103
 3104    if (parent.cmd.preproc != false) {
 3105        cstring *arg = &ary_Alloc(args);
 3106        *arg << "-preproc:";
 3107        bool_Print(parent.cmd.preproc, *arg);
 3108    }
 3109
 3110    if (parent.cmd.clean != false) {
 3111        cstring *arg = &ary_Alloc(args);
 3112        *arg << "-clean:";
 3113        bool_Print(parent.cmd.clean, *arg);
 3114    }
 3115
 3116    if (parent.cmd.dry_run != false) {
 3117        cstring *arg = &ary_Alloc(args);
 3118        *arg << "-dry_run:";
 3119        bool_Print(parent.cmd.dry_run, *arg);
 3120    }
 3121
 3122    if (parent.cmd.maxjobs != 0) {
 3123        cstring *arg = &ary_Alloc(args);
 3124        *arg << "-maxjobs:";
 3125        i32_Print(parent.cmd.maxjobs, *arg);
 3126    }
 3127
 3128    if (parent.cmd.printcmd != false) {
 3129        cstring *arg = &ary_Alloc(args);
 3130        *arg << "-printcmd:";
 3131        bool_Print(parent.cmd.printcmd, *arg);
 3132    }
 3133
 3134    if (parent.cmd.force != false) {
 3135        cstring *arg = &ary_Alloc(args);
 3136        *arg << "-force:";
 3137        bool_Print(parent.cmd.force, *arg);
 3138    }
 3139
 3140    if (parent.cmd.install != false) {
 3141        cstring *arg = &ary_Alloc(args);
 3142        *arg << "-install:";
 3143        bool_Print(parent.cmd.install, *arg);
 3144    }
 3145
 3146    if (parent.cmd.coverity != false) {
 3147        cstring *arg = &ary_Alloc(args);
 3148        *arg << "-coverity:";
 3149        bool_Print(parent.cmd.coverity, *arg);
 3150    }
 3151
 3152    if (parent.cmd.package != "") {
 3153        cstring *arg = &ary_Alloc(args);
 3154        *arg << "-package:";
 3155        cstring_Print(parent.cmd.package, *arg);
 3156    }
 3157
 3158    if (parent.cmd.maxerr != 100) {
 3159        cstring *arg = &ary_Alloc(args);
 3160        *arg << "-maxerr:";
 3161        u32_Print(parent.cmd.maxerr, *arg);
 3162    }
 3163
 3164    if (parent.cmd.disas.expr != "") {
 3165        cstring *arg = &ary_Alloc(args);
 3166        *arg << "-disas:";
 3167        command::disas_Print(parent.cmd, *arg);
 3168    }
 3169
 3170    if (parent.cmd.report != true) {
 3171        cstring *arg = &ary_Alloc(args);
 3172        *arg << "-report:";
 3173        bool_Print(parent.cmd.report, *arg);
 3174    }
 3175
 3176    if (parent.cmd.jcdb != "") {
 3177        cstring *arg = &ary_Alloc(args);
 3178        *arg << "-jcdb:";
 3179        cstring_Print(parent.cmd.jcdb, *arg);
 3180    }
 3181
 3182    if (parent.cmd.cache != 0) {
 3183        cstring *arg = &ary_Alloc(args);
 3184        *arg << "-cache:";
 3185        command::cache_Print(parent.cmd, *arg);
 3186    }
 3187    for (int i=1; i < algo_lib::_db.cmdline.verbose; ++i) {
 3188        ary_Alloc(args) << "-verbose";
 3189    }
 3190}
 3191
 3192// --- command.abt_proc..Uninit
 3193void command::abt_proc_Uninit(command::abt_proc& parent) {
 3194    command::abt_proc &row = parent; (void)row;
 3195
 3196    // command.abt_proc.abt.Uninit (Exec)  //
 3197    abt_Kill(parent); // kill child, ensure forward progress
 3198}
 3199
 3200// --- command.acr.where.Addary
 3201// Reserve space (this may move memory). Insert N element at the end.
 3202// Return aryptr to newly inserted block.
 3203// If the RHS argument aliases the array (refers to the same memory), exit program with fatal error.
 3204algo::aryptr<algo::cstring> command::where_Addary(command::acr& parent, algo::aryptr<algo::cstring> rhs) {
 3205    bool overlaps = rhs.n_elems>0 && rhs.elems >= parent.where_elems && rhs.elems < parent.where_elems + parent.where_max;
 3206    if (UNLIKELY(overlaps)) {
 3207        FatalErrorExit("command.tary_alias  field:command.acr.where  comment:'alias error: sub-array is being appended to the whole'");
 3208    }
 3209    int nnew = rhs.n_elems;
 3210    where_Reserve(parent, nnew); // reserve space
 3211    int at = parent.where_n;
 3212    for (int i = 0; i < nnew; i++) {
 3213        new (parent.where_elems + at + i) algo::cstring(rhs[i]);
 3214        parent.where_n++;
 3215    }
 3216    return algo::aryptr<algo::cstring>(parent.where_elems + at, nnew);
 3217}
 3218
 3219// --- command.acr.where.Alloc
 3220// Reserve space. Insert element at the end
 3221// The new element is initialized to a default value
 3222algo::cstring& command::where_Alloc(command::acr& parent) {
 3223    where_Reserve(parent, 1);
 3224    int n  = parent.where_n;
 3225    int at = n;
 3226    algo::cstring *elems = parent.where_elems;
 3227    new (elems + at) algo::cstring(); // construct new element, default initializer
 3228    parent.where_n = n+1;
 3229    return elems[at];
 3230}
 3231
 3232// --- command.acr.where.AllocAt
 3233// Reserve space for new element, reallocating the array if necessary
 3234// Insert new element at specified index. Index must be in range or a fatal error occurs.
 3235algo::cstring& command::where_AllocAt(command::acr& parent, int at) {
 3236    where_Reserve(parent, 1);
 3237    int n  = parent.where_n;
 3238    if (UNLIKELY(u64(at) >= u64(n+1))) {
 3239        FatalErrorExit("command.bad_alloc_at  field:command.acr.where  comment:'index out of range'");
 3240    }
 3241    algo::cstring *elems = parent.where_elems;
 3242    memmove(elems + at + 1, elems + at, (n - at) * sizeof(algo::cstring));
 3243    new (elems + at) algo::cstring(); // construct element, default initializer
 3244    parent.where_n = n+1;
 3245    return elems[at];
 3246}
 3247
 3248// --- command.acr.where.AllocN
 3249// Reserve space. Insert N elements at the end of the array, return pointer to array
 3250algo::aryptr<algo::cstring> command::where_AllocN(command::acr& parent, int n_elems) {
 3251    where_Reserve(parent, n_elems);
 3252    int old_n  = parent.where_n;
 3253    int new_n = old_n + n_elems;
 3254    algo::cstring *elems = parent.where_elems;
 3255    for (int i = old_n; i < new_n; i++) {
 3256        new (elems + i) algo::cstring(); // construct new element, default initialize
 3257    }
 3258    parent.where_n = new_n;
 3259    return algo::aryptr<algo::cstring>(elems + old_n, n_elems);
 3260}
 3261
 3262// --- command.acr.where.Remove
 3263// Remove item by index. If index outside of range, do nothing.
 3264void command::where_Remove(command::acr& parent, u32 i) {
 3265    u32 lim = parent.where_n;
 3266    algo::cstring *elems = parent.where_elems;
 3267    if (i < lim) {
 3268        elems[i].~cstring(); // destroy element
 3269        memmove(elems + i, elems + (i + 1), sizeof(algo::cstring) * (lim - (i + 1)));
 3270        parent.where_n = lim - 1;
 3271    }
 3272}
 3273
 3274// --- command.acr.where.RemoveAll
 3275void command::where_RemoveAll(command::acr& parent) {
 3276    u32 n = parent.where_n;
 3277    while (n > 0) {
 3278        n -= 1;
 3279        parent.where_elems[n].~cstring();
 3280        parent.where_n = n;
 3281    }
 3282}
 3283
 3284// --- command.acr.where.RemoveLast
 3285// Delete last element of array. Do nothing if array is empty.
 3286void command::where_RemoveLast(command::acr& parent) {
 3287    u64 n = parent.where_n;
 3288    if (n > 0) {
 3289        n -= 1;
 3290        where_qFind(parent, u64(n)).~cstring();
 3291        parent.where_n = n;
 3292    }
 3293}
 3294
 3295// --- command.acr.where.AbsReserve
 3296// Make sure N elements fit in array. Process dies if out of memory
 3297void command::where_AbsReserve(command::acr& parent, int n) {
 3298    u32 old_max  = parent.where_max;
 3299    if (n > i32(old_max)) {
 3300        u32 new_max  = i32_Max(i32_Max(old_max * 2, n), 4);
 3301        void *new_mem = algo_lib::malloc_ReallocMem(parent.where_elems, old_max * sizeof(algo::cstring), new_max * sizeof(algo::cstring));
 3302        if (UNLIKELY(!new_mem)) {
 3303            FatalErrorExit("command.tary_nomem  field:command.acr.where  comment:'out of memory'");
 3304        }
 3305        parent.where_elems = (algo::cstring*)new_mem;
 3306        parent.where_max = new_max;
 3307    }
 3308}
 3309
 3310// --- command.acr.where.Setary
 3311// Copy contents of RHS to PARENT.
 3312void command::where_Setary(command::acr& parent, command::acr &rhs) {
 3313    where_RemoveAll(parent);
 3314    int nnew = rhs.where_n;
 3315    where_Reserve(parent, nnew); // reserve space
 3316    for (int i = 0; i < nnew; i++) { // copy elements over
 3317        new (parent.where_elems + i) algo::cstring(where_qFind(rhs, i));
 3318        parent.where_n = i + 1;
 3319    }
 3320}
 3321
 3322// --- command.acr.where.Setary2
 3323// Copy specified array into where, discarding previous contents.
 3324// If the RHS argument aliases the array (refers to the same memory), throw exception.
 3325void command::where_Setary(command::acr& parent, const algo::aryptr<algo::cstring> &rhs) {
 3326    where_RemoveAll(parent);
 3327    where_Addary(parent, rhs);
 3328}
 3329
 3330// --- command.acr.where.AllocNVal
 3331// Reserve space. Insert N elements at the end of the array, return pointer to array
 3332algo::aryptr<algo::cstring> command::where_AllocNVal(command::acr& parent, int n_elems, const algo::cstring& val) {
 3333    where_Reserve(parent, n_elems);
 3334    int old_n  = parent.where_n;
 3335    int new_n = old_n + n_elems;
 3336    algo::cstring *elems = parent.where_elems;
 3337    for (int i = old_n; i < new_n; i++) {
 3338        new (elems + i) algo::cstring(val);
 3339    }
 3340    parent.where_n = new_n;
 3341    return algo::aryptr<algo::cstring>(elems + old_n, n_elems);
 3342}
 3343
 3344// --- command.acr.where.ReadStrptrMaybe
 3345// A single element is read from input string and appended to the array.
 3346// If the string contains an error, the array is untouched.
 3347// Function returns success value.
 3348bool command::where_ReadStrptrMaybe(command::acr& parent, algo::strptr in_str) {
 3349    bool retval = true;
 3350    algo::cstring &elem = where_Alloc(parent);
 3351    retval = algo::cstring_ReadStrptrMaybe(elem, in_str);
 3352    if (!retval) {
 3353        where_RemoveLast(parent);
 3354    }
 3355    return retval;
 3356}
 3357
 3358// --- command.acr.field.Addary
 3359// Reserve space (this may move memory). Insert N element at the end.
 3360// Return aryptr to newly inserted block.
 3361// If the RHS argument aliases the array (refers to the same memory), exit program with fatal error.
 3362algo::aryptr<algo::cstring> command::field_Addary(command::acr& parent, algo::aryptr<algo::cstring> rhs) {
 3363    bool overlaps = rhs.n_elems>0 && rhs.elems >= parent.field_elems && rhs.elems < parent.field_elems + parent.field_max;
 3364    if (UNLIKELY(overlaps)) {
 3365        FatalErrorExit("command.tary_alias  field:command.acr.field  comment:'alias error: sub-array is being appended to the whole'");
 3366    }
 3367    int nnew = rhs.n_elems;
 3368    field_Reserve(parent, nnew); // reserve space
 3369    int at = parent.field_n;
 3370    for (int i = 0; i < nnew; i++) {
 3371        new (parent.field_elems + at + i) algo::cstring(rhs[i]);
 3372        parent.field_n++;
 3373    }
 3374    return algo::aryptr<algo::cstring>(parent.field_elems + at, nnew);
 3375}
 3376
 3377// --- command.acr.field.Alloc
 3378// Reserve space. Insert element at the end
 3379// The new element is initialized to a default value
 3380algo::cstring& command::field_Alloc(command::acr& parent) {
 3381    field_Reserve(parent, 1);
 3382    int n  = parent.field_n;
 3383    int at = n;
 3384    algo::cstring *elems = parent.field_elems;
 3385    new (elems + at) algo::cstring(); // construct new element, default initializer
 3386    parent.field_n = n+1;
 3387    return elems[at];
 3388}
 3389
 3390// --- command.acr.field.AllocAt
 3391// Reserve space for new element, reallocating the array if necessary
 3392// Insert new element at specified index. Index must be in range or a fatal error occurs.
 3393algo::cstring& command::field_AllocAt(command::acr& parent, int at) {
 3394    field_Reserve(parent, 1);
 3395    int n  = parent.field_n;
 3396    if (UNLIKELY(u64(at) >= u64(n+1))) {
 3397        FatalErrorExit("command.bad_alloc_at  field:command.acr.field  comment:'index out of range'");
 3398    }
 3399    algo::cstring *elems = parent.field_elems;
 3400    memmove(elems + at + 1, elems + at, (n - at) * sizeof(algo::cstring));
 3401    new (elems + at) algo::cstring(); // construct element, default initializer
 3402    parent.field_n = n+1;
 3403    return elems[at];
 3404}
 3405
 3406// --- command.acr.field.AllocN
 3407// Reserve space. Insert N elements at the end of the array, return pointer to array
 3408algo::aryptr<algo::cstring> command::field_AllocN(command::acr& parent, int n_elems) {
 3409    field_Reserve(parent, n_elems);
 3410    int old_n  = parent.field_n;
 3411    int new_n = old_n + n_elems;
 3412    algo::cstring *elems = parent.field_elems;
 3413    for (int i = old_n; i < new_n; i++) {
 3414        new (elems + i) algo::cstring(); // construct new element, default initialize
 3415    }
 3416    parent.field_n = new_n;
 3417    return algo::aryptr<algo::cstring>(elems + old_n, n_elems);
 3418}
 3419
 3420// --- command.acr.field.Remove
 3421// Remove item by index. If index outside of range, do nothing.
 3422void command::field_Remove(command::acr& parent, u32 i) {
 3423    u32 lim = parent.field_n;
 3424    algo::cstring *elems = parent.field_elems;
 3425    if (i < lim) {
 3426        elems[i].~cstring(); // destroy element
 3427        memmove(elems + i, elems + (i + 1), sizeof(algo::cstring) * (lim - (i + 1)));
 3428        parent.field_n = lim - 1;
 3429    }
 3430}
 3431
 3432// --- command.acr.field.RemoveAll
 3433void command::field_RemoveAll(command::acr& parent) {
 3434    u32 n = parent.field_n;
 3435    while (n > 0) {
 3436        n -= 1;
 3437        parent.field_elems[n].~cstring();
 3438        parent.field_n = n;
 3439    }
 3440}
 3441
 3442// --- command.acr.field.RemoveLast
 3443// Delete last element of array. Do nothing if array is empty.
 3444void command::field_RemoveLast(command::acr& parent) {
 3445    u64 n = parent.field_n;
 3446    if (n > 0) {
 3447        n -= 1;
 3448        field_qFind(parent, u64(n)).~cstring();
 3449        parent.field_n = n;
 3450    }
 3451}
 3452
 3453// --- command.acr.field.AbsReserve
 3454// Make sure N elements fit in array. Process dies if out of memory
 3455void command::field_AbsReserve(command::acr& parent, int n) {
 3456    u32 old_max  = parent.field_max;
 3457    if (n > i32(old_max)) {
 3458        u32 new_max  = i32_Max(i32_Max(old_max * 2, n), 4);
 3459        void *new_mem = algo_lib::malloc_ReallocMem(parent.field_elems, old_max * sizeof(algo::cstring), new_max * sizeof(algo::cstring));
 3460        if (UNLIKELY(!new_mem)) {
 3461            FatalErrorExit("command.tary_nomem  field:command.acr.field  comment:'out of memory'");
 3462        }
 3463        parent.field_elems = (algo::cstring*)new_mem;
 3464        parent.field_max = new_max;
 3465    }
 3466}
 3467
 3468// --- command.acr.field.Setary
 3469// Copy contents of RHS to PARENT.
 3470void command::field_Setary(command::acr& parent, command::acr &rhs) {
 3471    field_RemoveAll(parent);
 3472    int nnew = rhs.field_n;
 3473    field_Reserve(parent, nnew); // reserve space
 3474    for (int i = 0; i < nnew; i++) { // copy elements over
 3475        new (parent.field_elems + i) algo::cstring(field_qFind(rhs, i));
 3476        parent.field_n = i + 1;
 3477    }
 3478}
 3479
 3480// --- command.acr.field.Setary2
 3481// Copy specified array into field, discarding previous contents.
 3482// If the RHS argument aliases the array (refers to the same memory), throw exception.
 3483void command::field_Setary(command::acr& parent, const algo::aryptr<algo::cstring> &rhs) {
 3484    field_RemoveAll(parent);
 3485    field_Addary(parent, rhs);
 3486}
 3487
 3488// --- command.acr.field.AllocNVal
 3489// Reserve space. Insert N elements at the end of the array, return pointer to array
 3490algo::aryptr<algo::cstring> command::field_AllocNVal(command::acr& parent, int n_elems, const algo::cstring& val) {
 3491    field_Reserve(parent, n_elems);
 3492    int old_n  = parent.field_n;
 3493    int new_n = old_n + n_elems;
 3494    algo::cstring *elems = parent.field_elems;
 3495    for (int i = old_n; i < new_n; i++) {
 3496        new (elems + i) algo::cstring(val);
 3497    }
 3498    parent.field_n = new_n;
 3499    return algo::aryptr<algo::cstring>(elems + old_n, n_elems);
 3500}
 3501
 3502// --- command.acr.field.ReadStrptrMaybe
 3503// A single element is read from input string and appended to the array.
 3504// If the string contains an error, the array is untouched.
 3505// Function returns success value.
 3506bool command::field_ReadStrptrMaybe(command::acr& parent, algo::strptr in_str) {
 3507    bool retval = true;
 3508    algo::cstring &elem = field_Alloc(parent);
 3509    retval = algo::cstring_ReadStrptrMaybe(elem, in_str);
 3510    if (!retval) {
 3511        field_RemoveLast(parent);
 3512    }
 3513    return retval;
 3514}
 3515
 3516// --- command.acr..ReadFieldMaybe
 3517bool command::acr_ReadFieldMaybe(command::acr& parent, algo::strptr field, algo::strptr strval) {
 3518    bool retval = true;
 3519    command::FieldId field_id;
 3520    (void)value_SetStrptrMaybe(field_id,algo::Pathcomp(field, ".LL"));
 3521    switch(field_id) {
 3522        case command_FieldId_query: {
 3523            retval = algo::cstring_ReadStrptrMaybe(parent.query, strval);
 3524            break;
 3525        }
 3526        case command_FieldId_where: {
 3527            retval = where_ReadStrptrMaybe(parent, strval);
 3528            break;
 3529        }
 3530        case command_FieldId_in: {
 3531            retval = algo::cstring_ReadStrptrMaybe(parent.in, strval);
 3532            break;
 3533        }
 3534        case command_FieldId_del: {
 3535            retval = bool_ReadStrptrMaybe(parent.del, strval);
 3536            break;
 3537        }
 3538        case command_FieldId_sel: {
 3539            retval = bool_ReadStrptrMaybe(parent.sel, strval);
 3540            break;
 3541        }
 3542        case command_FieldId_insert: {
 3543            retval = bool_ReadStrptrMaybe(parent.insert, strval);
 3544            break;
 3545        }
 3546        case command_FieldId_replace: {
 3547            retval = bool_ReadStrptrMaybe(parent.replace, strval);
 3548            break;
 3549        }
 3550        case command_FieldId_update: {
 3551            retval = bool_ReadStrptrMaybe(parent.update, strval);
 3552            break;
 3553        }
 3554        case command_FieldId_merge: {
 3555            retval = bool_ReadStrptrMaybe(parent.merge, strval);
 3556            break;
 3557        }
 3558        case command_FieldId_unused: {
 3559            retval = bool_ReadStrptrMaybe(parent.unused, strval);
 3560            break;
 3561        }
 3562        case command_FieldId_trunc: {
 3563            retval = bool_ReadStrptrMaybe(parent.trunc, strval);
 3564            break;
 3565        }
 3566        case command_FieldId_check: {
 3567            retval = bool_ReadStrptrMaybe(parent.check, strval);
 3568            break;
 3569        }
 3570        case command_FieldId_selerr: {
 3571            retval = bool_ReadStrptrMaybe(parent.selerr, strval);
 3572            break;
 3573        }
 3574        case command_FieldId_maxshow: {
 3575            retval = i32_ReadStrptrMaybe(parent.maxshow, strval);
 3576            break;
 3577        }
 3578        case command_FieldId_write: {
 3579            retval = bool_ReadStrptrMaybe(parent.write, strval);
 3580            break;
 3581        }
 3582        case command_FieldId_rename: {
 3583            retval = algo::cstring_ReadStrptrMaybe(parent.rename, strval);
 3584            break;
 3585        }
 3586        case command_FieldId_nup: {
 3587            retval = i32_ReadStrptrMaybe(parent.nup, strval);
 3588            break;
 3589        }
 3590        case command_FieldId_ndown: {
 3591            retval = i32_ReadStrptrMaybe(parent.ndown, strval);
 3592            break;
 3593        }
 3594        case command_FieldId_l: {
 3595            retval = bool_ReadStrptrMaybe(parent.l, strval);
 3596            break;
 3597        }
 3598        case command_FieldId_xref: {
 3599            retval = bool_ReadStrptrMaybe(parent.xref, strval);
 3600            break;
 3601        }
 3602        case command_FieldId_fldfunc: {
 3603            retval = bool_ReadStrptrMaybe(parent.fldfunc, strval);
 3604            break;
 3605        }
 3606        case command_FieldId_maxgroup: {
 3607            retval = i32_ReadStrptrMaybe(parent.maxgroup, strval);
 3608            break;
 3609        }
 3610        case command_FieldId_pretty: {
 3611            retval = bool_ReadStrptrMaybe(parent.pretty, strval);
 3612            break;
 3613        }
 3614        case command_FieldId_tree: {
 3615            retval = bool_ReadStrptrMaybe(parent.tree, strval);
 3616            break;
 3617        }
 3618        case command_FieldId_loose: {
 3619            retval = bool_ReadStrptrMaybe(parent.loose, strval);
 3620            break;
 3621        }
 3622        case command_FieldId_my: {
 3623            retval = bool_ReadStrptrMaybe(parent.my, strval);
 3624            break;
 3625        }
 3626        case command_FieldId_schema: {
 3627            retval = algo::cstring_ReadStrptrMaybe(parent.schema, strval);
 3628            break;
 3629        }
 3630        case command_FieldId_e: {
 3631            retval = bool_ReadStrptrMaybe(parent.e, strval);
 3632            break;
 3633        }
 3634        case command_FieldId_t: {
 3635            retval = bool_ReadStrptrMaybe(parent.t, strval);
 3636            break;
 3637        }
 3638        case command_FieldId_g: {
 3639            retval = bool_ReadStrptrMaybe(parent.g, strval);
 3640            break;
 3641        }
 3642        case command_FieldId_x: {
 3643            retval = bool_ReadStrptrMaybe(parent.x, strval);
 3644            break;
 3645        }
 3646        case command_FieldId_rowid: {
 3647            retval = bool_ReadStrptrMaybe(parent.rowid, strval);
 3648            break;
 3649        }
 3650        case command_FieldId_cmt: {
 3651            retval = bool_ReadStrptrMaybe(parent.cmt, strval);
 3652            break;
 3653        }
 3654        case command_FieldId_report: {
 3655            retval = bool_ReadStrptrMaybe(parent.report, strval);
 3656            break;
 3657        }
 3658        case command_FieldId_print: {
 3659            retval = bool_ReadStrptrMaybe(parent.print, strval);
 3660            break;
 3661        }
 3662        case command_FieldId_cmd: {
 3663            retval = algo::cstring_ReadStrptrMaybe(parent.cmd, strval);
 3664            break;
 3665        }
 3666        case command_FieldId_field: {
 3667            retval = field_ReadStrptrMaybe(parent, strval);
 3668            break;
 3669        }
 3670        case command_FieldId_regxof: {
 3671            retval = algo::cstring_ReadStrptrMaybe(parent.regxof, strval);
 3672            break;
 3673        }
 3674        case command_FieldId_meta: {
 3675            retval = bool_ReadStrptrMaybe(parent.meta, strval);
 3676            break;
 3677        }
 3678        default: break;
 3679    }
 3680    if (!retval) {
 3681        algo_lib::AppendErrtext("attr",field);
 3682    }
 3683    return retval;
 3684}
 3685
 3686// --- command.acr..ReadTupleMaybe
 3687// Read fields of command::acr from attributes of ascii tuple TUPLE
 3688bool command::acr_ReadTupleMaybe(command::acr &parent, algo::Tuple &tuple) {
 3689    bool retval = true;
 3690    int anon_idx = 0;
 3691    ind_beg(algo::Tuple_attrs_curs,attr,tuple) {
 3692        if (ch_N(attr.name) == 0) {
 3693            attr.name = acr_GetAnon(parent, anon_idx++);
 3694        }
 3695        retval = acr_ReadFieldMaybe(parent, attr.name, attr.value);
 3696        if (!retval) {
 3697            break;
 3698        }
 3699    }ind_end;
 3700    return retval;
 3701}
 3702
 3703// --- command.acr..Init
 3704// Set all fields to initial values.
 3705void command::acr_Init(command::acr& parent) {
 3706    parent.query = algo::strptr("");
 3707    parent.where_elems 	= 0; // (command.acr.where)
 3708    parent.where_n     	= 0; // (command.acr.where)
 3709    parent.where_max   	= 0; // (command.acr.where)
 3710    parent.in = algo::strptr("data");
 3711    parent.del = bool(false);
 3712    parent.sel = bool(false);
 3713    parent.insert = bool(false);
 3714    parent.replace = bool(false);
 3715    parent.update = bool(false);
 3716    parent.merge = bool(false);
 3717    parent.unused = bool(false);
 3718    parent.trunc = bool(false);
 3719    parent.check = bool(false);
 3720    parent.selerr = bool(true);
 3721    parent.maxshow = i32(100);
 3722    parent.write = bool(false);
 3723    parent.rename = algo::strptr("");
 3724    parent.nup = i32(0);
 3725    parent.ndown = i32(0);
 3726    parent.l = bool(false);
 3727    parent.xref = bool(false);
 3728    parent.fldfunc = bool(false);
 3729    parent.maxgroup = i32(25);
 3730    parent.pretty = bool(true);
 3731    parent.tree = bool(false);
 3732    parent.loose = bool(false);
 3733    parent.my = bool(false);
 3734    parent.schema = algo::strptr("data");
 3735    parent.e = bool(false);
 3736    parent.t = bool(false);
 3737    parent.g = bool(false);
 3738    parent.x = bool(false);
 3739    parent.rowid = bool(false);
 3740    parent.cmt = bool(false);
 3741    parent.report = bool(true);
 3742    parent.print = bool(true);
 3743    parent.cmd = algo::strptr("");
 3744    parent.field_elems 	= 0; // (command.acr.field)
 3745    parent.field_n     	= 0; // (command.acr.field)
 3746    parent.field_max   	= 0; // (command.acr.field)
 3747    parent.regxof = algo::strptr("");
 3748    parent.meta = bool(false);
 3749}
 3750
 3751// --- command.acr..Uninit
 3752void command::acr_Uninit(command::acr& parent) {
 3753    command::acr &row = parent; (void)row;
 3754
 3755    // command.acr.field.Uninit (Tary)  //Fields to select
 3756    // remove all elements from command.acr.field
 3757    field_RemoveAll(parent);
 3758    // free memory for Tary command.acr.field
 3759    algo_lib::malloc_FreeMem(parent.field_elems, sizeof(algo::cstring)*parent.field_max); // (command.acr.field)
 3760
 3761    // command.acr.where.Uninit (Tary)  //Additional key:value pairs to match
 3762    // remove all elements from command.acr.where
 3763    where_RemoveAll(parent);
 3764    // free memory for Tary command.acr.where
 3765    algo_lib::malloc_FreeMem(parent.where_elems, sizeof(algo::cstring)*parent.where_max); // (command.acr.where)
 3766}
 3767
 3768// --- command.acr..ToCmdline
 3769// Convenience function that returns a full command line
 3770// Assume command is in a directory called bin
 3771tempstr command::acr_ToCmdline(command::acr& row) {
 3772    tempstr ret;
 3773    ret << "bin/acr ";
 3774    acr_PrintArgv(row, ret);
 3775    // inherit less intense verbose, debug options
 3776    for (int i = 1; i < algo_lib::_db.cmdline.verbose; i++) {
 3777        ret << " -verbose";
 3778    }
 3779    for (int i = 1; i < algo_lib::_db.cmdline.debug; i++) {
 3780        ret << " -debug";
 3781    }
 3782    return ret;
 3783}
 3784
 3785// --- command.acr..PrintArgv
 3786// print string representation of ROW to string STR
 3787// cfmt:command.acr.Argv  printfmt:Auto
 3788void command::acr_PrintArgv(command::acr& row, algo::cstring& str) {
 3789    algo::tempstr temp;
 3790    (void)temp;
 3791    (void)str;
 3792    ch_RemoveAll(temp);
 3793    cstring_Print(row.query, temp);
 3794    str << " -query:";
 3795    strptr_PrintBash(temp,str);
 3796    ind_beg(acr_where_curs,value,row) {
 3797        ch_RemoveAll(temp);
 3798        cstring_Print(value, temp);
 3799        str << " -where:";
 3800        strptr_PrintBash(temp,str);
 3801    }ind_end;
 3802    if (!(row.in == "data")) {
 3803        ch_RemoveAll(temp);
 3804        cstring_Print(row.in, temp);
 3805        str << " -in:";
 3806        strptr_PrintBash(temp,str);
 3807    }
 3808    if (!(row.del == false)) {
 3809        ch_RemoveAll(temp);
 3810        bool_Print(row.del, temp);
 3811        str << " -del:";
 3812        strptr_PrintBash(temp,str);
 3813    }
 3814    if (!(row.sel == false)) {
 3815        ch_RemoveAll(temp);
 3816        bool_Print(row.sel, temp);
 3817        str << " -sel:";
 3818        strptr_PrintBash(temp,str);
 3819    }
 3820    if (!(row.insert == false)) {
 3821        ch_RemoveAll(temp);
 3822        bool_Print(row.insert, temp);
 3823        str << " -insert:";
 3824        strptr_PrintBash(temp,str);
 3825    }
 3826    if (!(row.replace == false)) {
 3827        ch_RemoveAll(temp);
 3828        bool_Print(row.replace, temp);
 3829        str << " -replace:";
 3830        strptr_PrintBash(temp,str);
 3831    }
 3832    if (!(row.update == false)) {
 3833        ch_RemoveAll(temp);
 3834        bool_Print(row.update, temp);
 3835        str << " -update:";
 3836        strptr_PrintBash(temp,str);
 3837    }
 3838    if (!(row.merge == false)) {
 3839        ch_RemoveAll(temp);
 3840        bool_Print(row.merge, temp);
 3841        str << " -merge:";
 3842        strptr_PrintBash(temp,str);
 3843    }
 3844    if (!(row.unused == false)) {
 3845        ch_RemoveAll(temp);
 3846        bool_Print(row.unused, temp);
 3847        str << " -unused:";
 3848        strptr_PrintBash(temp,str);
 3849    }
 3850    if (!(row.trunc == false)) {
 3851        ch_RemoveAll(temp);
 3852        bool_Print(row.trunc, temp);
 3853        str << " -trunc:";
 3854        strptr_PrintBash(temp,str);
 3855    }
 3856    if (!(row.check == false)) {
 3857        ch_RemoveAll(temp);
 3858        bool_Print(row.check, temp);
 3859        str << " -check:";
 3860        strptr_PrintBash(temp,str);
 3861    }
 3862    if (!(row.selerr == true)) {
 3863        ch_RemoveAll(temp);
 3864        bool_Print(row.selerr, temp);
 3865        str << " -selerr:";
 3866        strptr_PrintBash(temp,str);
 3867    }
 3868    if (!(row.maxshow == 100)) {
 3869        ch_RemoveAll(temp);
 3870        i32_Print(row.maxshow, temp);
 3871        str << " -maxshow:";
 3872        strptr_PrintBash(temp,str);
 3873    }
 3874    if (!(row.write == false)) {
 3875        ch_RemoveAll(temp);
 3876        bool_Print(row.write, temp);
 3877        str << " -write:";
 3878        strptr_PrintBash(temp,str);
 3879    }
 3880    if (!(row.rename == "")) {
 3881        ch_RemoveAll(temp);
 3882        cstring_Print(row.rename, temp);
 3883        str << " -rename:";
 3884        strptr_PrintBash(temp,str);
 3885    }
 3886    if (!(row.nup == 0)) {
 3887        ch_RemoveAll(temp);
 3888        i32_Print(row.nup, temp);
 3889        str << " -nup:";
 3890        strptr_PrintBash(temp,str);
 3891    }
 3892    if (!(row.ndown == 0)) {
 3893        ch_RemoveAll(temp);
 3894        i32_Print(row.ndown, temp);
 3895        str << " -ndown:";
 3896        strptr_PrintBash(temp,str);
 3897    }
 3898    if (!(row.l == false)) {
 3899        ch_RemoveAll(temp);
 3900        bool_Print(row.l, temp);
 3901        str << " -l:";
 3902        strptr_PrintBash(temp,str);
 3903    }
 3904    if (!(row.xref == false)) {
 3905        ch_RemoveAll(temp);
 3906        bool_Print(row.xref, temp);
 3907        str << " -xref:";
 3908        strptr_PrintBash(temp,str);
 3909    }
 3910    if (!(row.fldfunc == false)) {
 3911        ch_RemoveAll(temp);
 3912        bool_Print(row.fldfunc, temp);
 3913        str << " -fldfunc:";
 3914        strptr_PrintBash(temp,str);
 3915    }
 3916    if (!(row.maxgroup == 25)) {
 3917        ch_RemoveAll(temp);
 3918        i32_Print(row.maxgroup, temp);
 3919        str << " -maxgroup:";
 3920        strptr_PrintBash(temp,str);
 3921    }
 3922    if (!(row.pretty == true)) {
 3923        ch_RemoveAll(temp);
 3924        bool_Print(row.pretty, temp);
 3925        str << " -pretty:";
 3926        strptr_PrintBash(temp,str);
 3927    }
 3928    if (!(row.tree == false)) {
 3929        ch_RemoveAll(temp);
 3930        bool_Print(row.tree, temp);
 3931        str << " -tree:";
 3932        strptr_PrintBash(temp,str);
 3933    }
 3934    if (!(row.loose == false)) {
 3935        ch_RemoveAll(temp);
 3936        bool_Print(row.loose, temp);
 3937        str << " -loose:";
 3938        strptr_PrintBash(temp,str);
 3939    }
 3940    if (!(row.my == false)) {
 3941        ch_RemoveAll(temp);
 3942        bool_Print(row.my, temp);
 3943        str << " -my:";
 3944        strptr_PrintBash(temp,str);
 3945    }
 3946    if (!(row.schema == "data")) {
 3947        ch_RemoveAll(temp);
 3948        cstring_Print(row.schema, temp);
 3949        str << " -schema:";
 3950        strptr_PrintBash(temp,str);
 3951    }
 3952    if (!(row.e == false)) {
 3953        ch_RemoveAll(temp);
 3954        bool_Print(row.e, temp);
 3955        str << " -e:";
 3956        strptr_PrintBash(temp,str);
 3957    }
 3958    if (!(row.t == false)) {
 3959        ch_RemoveAll(temp);
 3960        bool_Print(row.t, temp);
 3961        str << " -t:";
 3962        strptr_PrintBash(temp,str);
 3963    }
 3964    if (!(row.g == false)) {
 3965        ch_RemoveAll(temp);
 3966        bool_Print(row.g, temp);
 3967        str << " -g:";
 3968        strptr_PrintBash(temp,str);
 3969    }
 3970    if (!(row.x == false)) {
 3971        ch_RemoveAll(temp);
 3972        bool_Print(row.x, temp);
 3973        str << " -x:";
 3974        strptr_PrintBash(temp,str);
 3975    }
 3976    if (!(row.rowid == false)) {
 3977        ch_RemoveAll(temp);
 3978        bool_Print(row.rowid, temp);
 3979        str << " -rowid:";
 3980        strptr_PrintBash(temp,str);
 3981    }
 3982    if (!(row.cmt == false)) {
 3983        ch_RemoveAll(temp);
 3984        bool_Print(row.cmt, temp);
 3985        str << " -cmt:";
 3986        strptr_PrintBash(temp,str);
 3987    }
 3988    if (!(row.report == true)) {
 3989        ch_RemoveAll(temp);
 3990        bool_Print(row.report, temp);
 3991        str << " -report:";
 3992        strptr_PrintBash(temp,str);
 3993    }
 3994    if (!(row.print == true)) {
 3995        ch_RemoveAll(temp);
 3996        bool_Print(row.print, temp);
 3997        str << " -print:";
 3998        strptr_PrintBash(temp,str);
 3999    }
 4000    if (!(row.cmd == "")) {
 4001        ch_RemoveAll(temp);
 4002        cstring_Print(row.cmd, temp);
 4003        str << " -cmd:";
 4004        strptr_PrintBash(temp,str);
 4005    }
 4006    ind_beg(acr_field_curs,value,row) {
 4007        ch_RemoveAll(temp);
 4008        cstring_Print(value, temp);
 4009        str << " -field:";
 4010        strptr_PrintBash(temp,str);
 4011    }ind_end;
 4012    if (!(row.regxof == "")) {
 4013        ch_RemoveAll(temp);
 4014        cstring_Print(row.regxof, temp);
 4015        str << " -regxof:";
 4016        strptr_PrintBash(temp,str);
 4017    }
 4018    if (!(row.meta == false)) {
 4019        ch_RemoveAll(temp);
 4020        bool_Print(row.meta, temp);
 4021        str << " -meta:";
 4022        strptr_PrintBash(temp,str);
 4023    }
 4024}
 4025
 4026// --- command.acr..GetAnon
 4027algo::strptr command::acr_GetAnon(command::acr &parent, i32 idx) {
 4028    (void)parent;//only to avoid -Wunused-parameter
 4029    switch(idx) {
 4030        case(0): return strptr("query", 5);
 4031        default: return algo::strptr();
 4032    }
 4033}
 4034
 4035// --- command.acr..NArgs
 4036// Used with command lines
 4037// Return # of command-line arguments that must follow this argument
 4038// If FIELD is invalid, return -1
 4039i32 command::acr_NArgs(command::FieldId field, algo::strptr& out_dflt, bool* out_anon) {
 4040    i32 retval = 1;
 4041    switch (field) {
 4042        case command_FieldId_query: { // $comment
 4043            *out_anon = true;
 4044        } break;
 4045        case command_FieldId_where: { // $comment
 4046            *out_anon = false;
 4047        } break;
 4048        case command_FieldId_in: { // $comment
 4049            *out_anon = false;
 4050        } break;
 4051        case command_FieldId_del: { // $comment
 4052            *out_anon = false;
 4053            retval=0;
 4054            out_dflt="Y";
 4055        } break;
 4056        case command_FieldId_sel: { // bool: no argument required but value may be specified as del:Y
 4057            *out_anon = false;
 4058            retval=0;
 4059            out_dflt="Y";
 4060        } break;
 4061        case command_FieldId_insert: { // bool: no argument required but value may be specified as sel:Y
 4062            *out_anon = false;
 4063            retval=0;
 4064            out_dflt="Y";
 4065        } break;
 4066        case command_FieldId_replace: { // bool: no argument required but value may be specified as insert:Y
 4067            *out_anon = false;
 4068            retval=0;
 4069            out_dflt="Y";
 4070        } break;
 4071        case command_FieldId_update: { // bool: no argument required but value may be specified as replace:Y
 4072            *out_anon = false;
 4073            retval=0;
 4074            out_dflt="Y";
 4075        } break;
 4076        case command_FieldId_merge: { // bool: no argument required but value may be specified as update:Y
 4077            *out_anon = false;
 4078            retval=0;
 4079            out_dflt="Y";
 4080        } break;
 4081        case command_FieldId_unused: { // bool: no argument required but value may be specified as merge:Y
 4082            *out_anon = false;
 4083            retval=0;
 4084            out_dflt="Y";
 4085        } break;
 4086        case command_FieldId_trunc: { // bool: no argument required but value may be specified as unused:Y
 4087            *out_anon = false;
 4088            retval=0;
 4089            out_dflt="Y";
 4090        } break;
 4091        case command_FieldId_check: { // bool: no argument required but value may be specified as trunc:Y
 4092            *out_anon = false;
 4093            retval=0;
 4094            out_dflt="Y";
 4095        } break;
 4096        case command_FieldId_selerr: { // bool: no argument required but value may be specified as check:Y
 4097            *out_anon = false;
 4098            retval=0;
 4099            out_dflt="Y";
 4100        } break;
 4101        case command_FieldId_maxshow: { // bool: no argument required but value may be specified as selerr:Y
 4102            *out_anon = false;
 4103        } break;
 4104        case command_FieldId_write: { // bool: no argument required but value may be specified as selerr:Y
 4105            *out_anon = false;
 4106            retval=0;
 4107            out_dflt="Y";
 4108        } break;
 4109        case command_FieldId_rename: { // bool: no argument required but value may be specified as write:Y
 4110            *out_anon = false;
 4111        } break;
 4112        case command_FieldId_nup: { // bool: no argument required but value may be specified as write:Y
 4113            *out_anon = false;
 4114        } break;
 4115        case command_FieldId_ndown: { // bool: no argument required but value may be specified as write:Y
 4116            *out_anon = false;
 4117        } break;
 4118        case command_FieldId_l: { // bool: no argument required but value may be specified as write:Y
 4119            *out_anon = false;
 4120            retval=0;
 4121            out_dflt="Y";
 4122        } break;
 4123        case command_FieldId_xref: { // bool: no argument required but value may be specified as l:Y
 4124            *out_anon = false;
 4125            retval=0;
 4126            out_dflt="Y";
 4127        } break;
 4128        case command_FieldId_fldfunc: { // bool: no argument required but value may be specified as xref:Y
 4129            *out_anon = false;
 4130            retval=0;
 4131            out_dflt="Y";
 4132        } break;
 4133        case command_FieldId_maxgroup: { // bool: no argument required but value may be specified as fldfunc:Y
 4134            *out_anon = false;
 4135        } break;
 4136        case command_FieldId_pretty: { // bool: no argument required but value may be specified as fldfunc:Y
 4137            *out_anon = false;
 4138            retval=0;
 4139            out_dflt="Y";
 4140        } break;
 4141        case command_FieldId_tree: { // bool: no argument required but value may be specified as pretty:Y
 4142            *out_anon = false;
 4143            retval=0;
 4144            out_dflt="Y";
 4145        } break;
 4146        case command_FieldId_loose: { // bool: no argument required but value may be specified as tree:Y
 4147            *out_anon = false;
 4148            retval=0;
 4149            out_dflt="Y";
 4150        } break;
 4151        case command_FieldId_my: { // bool: no argument required but value may be specified as loose:Y
 4152            *out_anon = false;
 4153            retval=0;
 4154            out_dflt="Y";
 4155        } break;
 4156        case command_FieldId_schema: { // bool: no argument required but value may be specified as my:Y
 4157            *out_anon = false;
 4158        } break;
 4159        case command_FieldId_e: { // bool: no argument required but value may be specified as my:Y
 4160            *out_anon = false;
 4161            retval=0;
 4162            out_dflt="Y";
 4163        } break;
 4164        case command_FieldId_t: { // bool: no argument required but value may be specified as e:Y
 4165            *out_anon = false;
 4166            retval=0;
 4167            out_dflt="Y";
 4168        } break;
 4169        case command_FieldId_g: { // bool: no argument required but value may be specified as t:Y
 4170            *out_anon = false;
 4171            retval=0;
 4172            out_dflt="Y";
 4173        } break;
 4174        case command_FieldId_x: { // bool: no argument required but value may be specified as g:Y
 4175            *out_anon = false;
 4176            retval=0;
 4177            out_dflt="Y";
 4178        } break;
 4179        case command_FieldId_rowid: { // bool: no argument required but value may be specified as x:Y
 4180            *out_anon = false;
 4181            retval=0;
 4182            out_dflt="Y";
 4183        } break;
 4184        case command_FieldId_cmt: { // bool: no argument required but value may be specified as rowid:Y
 4185            *out_anon = false;
 4186            retval=0;
 4187            out_dflt="Y";
 4188        } break;
 4189        case command_FieldId_report: { // bool: no argument required but value may be specified as cmt:Y
 4190            *out_anon = false;
 4191            retval=0;
 4192            out_dflt="Y";
 4193        } break;
 4194        case command_FieldId_print: { // bool: no argument required but value may be specified as report:Y
 4195            *out_anon = false;
 4196            retval=0;
 4197            out_dflt="Y";
 4198        } break;
 4199        case command_FieldId_cmd: { // bool: no argument required but value may be specified as print:Y
 4200            *out_anon = false;
 4201        } break;
 4202        case command_FieldId_field: { // bool: no argument required but value may be specified as print:Y
 4203            *out_anon = false;
 4204        } break;
 4205        case command_FieldId_regxof: { // bool: no argument required but value may be specified as print:Y
 4206            *out_anon = false;
 4207        } break;
 4208        case command_FieldId_meta: { // bool: no argument required but value may be specified as print:Y
 4209            *out_anon = false;
 4210            retval=0;
 4211            out_dflt="Y";
 4212        } break;
 4213        default:
 4214        retval=-1; // unrecognized
 4215    }
 4216    return retval;
 4217}
 4218
 4219// --- command.acr_compl..ReadFieldMaybe
 4220bool command::acr_compl_ReadFieldMaybe(command::acr_compl& parent, algo::strptr field, algo::strptr strval) {
 4221    bool retval = true;
 4222    command::FieldId field_id;
 4223    (void)value_SetStrptrMaybe(field_id,field);
 4224    switch(field_id) {
 4225        case command_FieldId_data: {
 4226            retval = algo::cstring_ReadStrptrMaybe(parent.data, strval);
 4227            break;
 4228        }
 4229        case command_FieldId_schema: {
 4230            retval = algo::cstring_ReadStrptrMaybe(parent.schema, strval);
 4231            break;
 4232        }
 4233        case command_FieldId_line: {
 4234            retval = algo::cstring_ReadStrptrMaybe(parent.line, strval);
 4235            break;
 4236        }
 4237        case command_FieldId_point: {
 4238            retval = algo::cstring_ReadStrptrMaybe(parent.point, strval);
 4239            break;
 4240        }
 4241        case command_FieldId_type: {
 4242            retval = algo::cstring_ReadStrptrMaybe(parent.type, strval);
 4243            break;
 4244        }
 4245        case command_FieldId_install: {
 4246            retval = bool_ReadStrptrMaybe(parent.install, strval);
 4247            break;
 4248        }
 4249        case command_FieldId_debug_log: {
 4250            retval = algo::cstring_ReadStrptrMaybe(parent.debug_log, strval);
 4251            break;
 4252        }
 4253        default: break;
 4254    }
 4255    if (!retval) {
 4256        algo_lib::AppendErrtext("attr",field);
 4257    }
 4258    return retval;
 4259}
 4260
 4261// --- command.acr_compl..ReadStrptrMaybe
 4262// Read fields of command::acr_compl from an ascii string.
 4263// The format of the string is an ssim Tuple
 4264bool command::acr_compl_ReadStrptrMaybe(command::acr_compl &parent, algo::strptr in_str) {
 4265    bool retval = true;
 4266    retval = algo::StripTypeTag(in_str, "command.acr_compl");
 4267    ind_beg(algo::Attr_curs, attr, in_str) {
 4268        retval = retval && acr_compl_ReadFieldMaybe(parent, attr.name, attr.value);
 4269    }ind_end;
 4270    return retval;
 4271}
 4272
 4273// --- command.acr_compl..ReadTupleMaybe
 4274// Read fields of command::acr_compl from attributes of ascii tuple TUPLE
 4275bool command::acr_compl_ReadTupleMaybe(command::acr_compl &parent, algo::Tuple &tuple) {
 4276    bool retval = true;
 4277    ind_beg(algo::Tuple_attrs_curs,attr,tuple) {
 4278        retval = acr_compl_ReadFieldMaybe(parent, attr.name, attr.value);
 4279        if (!retval) {
 4280            break;
 4281        }
 4282    }ind_end;
 4283    return retval;
 4284}
 4285
 4286// --- command.acr_compl..ToCmdline
 4287// Convenience function that returns a full command line
 4288// Assume command is in a directory called bin
 4289tempstr command::acr_compl_ToCmdline(command::acr_compl& row) {
 4290    tempstr ret;
 4291    ret << "bin/acr_compl ";
 4292    acr_compl_PrintArgv(row, ret);
 4293    // inherit less intense verbose, debug options
 4294    for (int i = 1; i < algo_lib::_db.cmdline.verbose; i++) {
 4295        ret << " -verbose";
 4296    }
 4297    for (int i = 1; i < algo_lib::_db.cmdline.debug; i++) {
 4298        ret << " -debug";
 4299    }
 4300    return ret;
 4301}
 4302
 4303// --- command.acr_compl..PrintArgv
 4304// print string representation of ROW to string STR
 4305// cfmt:command.acr_compl.Argv  printfmt:Auto
 4306void command::acr_compl_PrintArgv(command::acr_compl& row, algo::cstring& str) {
 4307    algo::tempstr temp;
 4308    (void)temp;
 4309    (void)str;
 4310    if (!(row.data == "data")) {
 4311        ch_RemoveAll(temp);
 4312        cstring_Print(row.data, temp);
 4313        str << " -data:";
 4314        strptr_PrintBash(temp,str);
 4315    }
 4316    if (!(row.schema == "data")) {
 4317        ch_RemoveAll(temp);
 4318        cstring_Print(row.schema, temp);
 4319        str << " -schema:";
 4320        strptr_PrintBash(temp,str);
 4321    }
 4322    if (!(row.line == "")) {
 4323        ch_RemoveAll(temp);
 4324        cstring_Print(row.line, temp);
 4325        str << " -line:";
 4326        strptr_PrintBash(temp,str);
 4327    }
 4328    if (!(row.point == "")) {
 4329        ch_RemoveAll(temp);
 4330        cstring_Print(row.point, temp);
 4331        str << " -point:";
 4332        strptr_PrintBash(temp,str);
 4333    }
 4334    if (!(row.type == "9")) {
 4335        ch_RemoveAll(temp);
 4336        cstring_Print(row.type, temp);
 4337        str << " -type:";
 4338        strptr_PrintBash(temp,str);
 4339    }
 4340    if (!(row.install == false)) {
 4341        ch_RemoveAll(temp);
 4342        bool_Print(row.install, temp);
 4343        str << " -install:";
 4344        strptr_PrintBash(temp,str);
 4345    }
 4346    if (!(row.debug_log == "")) {
 4347        ch_RemoveAll(temp);
 4348        cstring_Print(row.debug_log, temp);
 4349        str << " -debug_log:";
 4350        strptr_PrintBash(temp,str);
 4351    }
 4352}
 4353
 4354// --- command.acr_compl..Print
 4355// print string representation of ROW to string STR
 4356// cfmt:command.acr_compl.String  printfmt:Tuple
 4357void command::acr_compl_Print(command::acr_compl& row, algo::cstring& str) {
 4358    algo::tempstr temp;
 4359    str << "command.acr_compl";
 4360
 4361    algo::cstring_Print(row.data, temp);
 4362    PrintAttrSpaceReset(str,"data", temp);
 4363
 4364    algo::cstring_Print(row.schema, temp);
 4365    PrintAttrSpaceReset(str,"schema", temp);
 4366
 4367    algo::cstring_Print(row.line, temp);
 4368    PrintAttrSpaceReset(str,"line", temp);
 4369
 4370    algo::cstring_Print(row.point, temp);
 4371    PrintAttrSpaceReset(str,"point", temp);
 4372
 4373    algo::cstring_Print(row.type, temp);
 4374    PrintAttrSpaceReset(str,"type", temp);
 4375
 4376    bool_Print(row.install, temp);
 4377    PrintAttrSpaceReset(str,"install", temp);
 4378
 4379    algo::cstring_Print(row.debug_log, temp);
 4380    PrintAttrSpaceReset(str,"debug_log", temp);
 4381}
 4382
 4383// --- command.acr_compl..NArgs
 4384// Used with command lines
 4385// Return # of command-line arguments that must follow this argument
 4386// If FIELD is invalid, return -1
 4387i32 command::acr_compl_NArgs(command::FieldId field, algo::strptr& out_dflt, bool* out_anon) {
 4388    i32 retval = 1;
 4389    switch (field) {
 4390        case command_FieldId_data: { // $comment
 4391            *out_anon = false;
 4392        } break;
 4393        case command_FieldId_schema: { // $comment
 4394            *out_anon = false;
 4395        } break;
 4396        case command_FieldId_line: { // $comment
 4397            *out_anon = false;
 4398        } break;
 4399        case command_FieldId_point: { // $comment
 4400            *out_anon = false;
 4401        } break;
 4402        case command_FieldId_type: { // $comment
 4403            *out_anon = false;
 4404        } break;
 4405        case command_FieldId_install: { // $comment
 4406            *out_anon = false;
 4407            retval=0;
 4408            out_dflt="Y";
 4409        } break;
 4410        case command_FieldId_debug_log: { // bool: no argument required but value may be specified as install:Y
 4411            *out_anon = false;
 4412        } break;
 4413        default:
 4414        retval=-1; // unrecognized
 4415    }
 4416    return retval;
 4417}
 4418
 4419// --- command.acr_compl_proc.acr_compl.Start
 4420// Start subprocess
 4421// If subprocess already running, do nothing. Otherwise, start it
 4422int command::acr_compl_Start(command::acr_compl_proc& parent) {
 4423    int retval = 0;
 4424    if (parent.pid == 0) {
 4425        verblog(acr_compl_ToCmdline(parent)); // maybe print command
 4426#ifdef WIN32
 4427        algo_lib::ResolveExecFname(parent.path);
 4428        tempstr cmdline(acr_compl_ToCmdline(parent));
 4429        parent.pid = dospawn(Zeroterm(parent.path),Zeroterm(cmdline),parent.timeout,parent.fstdin,parent.fstdout,parent.fstderr);
 4430#else
 4431        parent.pid = fork();
 4432        if (parent.pid == 0) { // child
 4433            algo_lib::DieWithParent();
 4434            if (parent.timeout > 0) {
 4435                alarm(parent.timeout);
 4436            }
 4437            if (retval==0) retval=algo_lib::ApplyRedirect(parent.fstdin , 0);
 4438            if (retval==0) retval=algo_lib::ApplyRedirect(parent.fstdout, 1);
 4439            if (retval==0) retval=algo_lib::ApplyRedirect(parent.fstderr, 2);
 4440            if (retval==0) retval= acr_compl_Execv(parent);
 4441            if (retval != 0) { // if start fails, print error
 4442                int err=errno;
 4443                prerr("command.acr_compl_execv"
 4444                <<Keyval("errno",err)
 4445                <<Keyval("errstr",strerror(err))
 4446                <<Keyval("comment","Execv failed"));
 4447            }
 4448            _exit(127); // if failed to start, exit anyway
 4449        } else if (parent.pid == -1) {
 4450            retval = errno; // failed to fork
 4451        }
 4452#endif
 4453    }
 4454    parent.status = parent.pid > 0 ? 0 : -1; // if didn't start, set error status
 4455    return retval;
 4456}
 4457
 4458// --- command.acr_compl_proc.acr_compl.StartRead
 4459// Start subprocess & Read output
 4460algo::Fildes command::acr_compl_StartRead(command::acr_compl_proc& parent, algo_lib::FFildes &read) {
 4461    int pipefd[2];
 4462    int rc=pipe(pipefd);
 4463    (void)rc;
 4464    read.fd.value = pipefd[0];
 4465    parent.fstdout  << ">&" << pipefd[1];
 4466    acr_compl_Start(parent);
 4467    (void)close(pipefd[1]);
 4468    return read.fd;
 4469}
 4470
 4471// --- command.acr_compl_proc.acr_compl.Kill
 4472// Kill subprocess and wait
 4473void command::acr_compl_Kill(command::acr_compl_proc& parent) {
 4474    if (parent.pid != 0) {
 4475        kill(parent.pid,9);
 4476        acr_compl_Wait(parent);
 4477    }
 4478}
 4479
 4480// --- command.acr_compl_proc.acr_compl.Wait
 4481// Wait for subprocess to return
 4482void command::acr_compl_Wait(command::acr_compl_proc& parent) {
 4483    if (parent.pid > 0) {
 4484        int wait_flags = 0;
 4485        int wait_status = 0;
 4486        int rc = -1;
 4487        do {
 4488            // really wait for subprocess to exit
 4489            rc = waitpid(parent.pid,&wait_status,wait_flags);
 4490        } while (rc==-1 && errno==EINTR);
 4491        if (rc == parent.pid) {
 4492            parent.status = wait_status;
 4493            parent.pid = 0;
 4494        }
 4495    }
 4496}
 4497
 4498// --- command.acr_compl_proc.acr_compl.Exec
 4499// Start + Wait
 4500// Execute subprocess and return exit code
 4501int command::acr_compl_Exec(command::acr_compl_proc& parent) {
 4502    acr_compl_Start(parent);
 4503    acr_compl_Wait(parent);
 4504    return parent.status;
 4505}
 4506
 4507// --- command.acr_compl_proc.acr_compl.ExecX
 4508// Start + Wait, throw exception on error
 4509// Execute subprocess; throw human-readable exception on error
 4510void command::acr_compl_ExecX(command::acr_compl_proc& parent) {
 4511    int rc = acr_compl_Exec(parent);
 4512    vrfy(rc==0, tempstr() << "algo_lib.exec" << Keyval("cmd",acr_compl_ToCmdline(parent))
 4513    << Keyval("comment",algo::DescribeWaitStatus(parent.status)));
 4514}
 4515
 4516// --- command.acr_compl_proc.acr_compl.Execv
 4517// Call execv()
 4518// Call execv with specified parameters
 4519int command::acr_compl_Execv(command::acr_compl_proc& parent) {
 4520    int ret = 0;
 4521    algo::StringAry args;
 4522    acr_compl_ToArgv(parent, args);
 4523    char **argv = (char**)alloca((ary_N(args)+1)*sizeof(*argv));
 4524    ind_beg(algo::StringAry_ary_curs,arg,args) {
 4525        argv[ind_curs(arg).index] = Zeroterm(arg);
 4526    }ind_end;
 4527    argv[ary_N(args)] = NULL;
 4528    // if parent.path is relative, search for it in PATH
 4529    algo_lib::ResolveExecFname(parent.path);
 4530    ret = execv(Zeroterm(parent.path),argv);
 4531    return ret;
 4532}
 4533
 4534// --- command.acr_compl_proc.acr_compl.ToCmdline
 4535algo::tempstr command::acr_compl_ToCmdline(command::acr_compl_proc& parent) {
 4536    algo::tempstr retval;
 4537    retval << parent.path << " ";
 4538    command::acr_compl_PrintArgv(parent.cmd,retval);
 4539    if (ch_N(parent.fstdin)) {
 4540        retval << " " << parent.fstdin;
 4541    }
 4542    if (ch_N(parent.fstdout)) {
 4543        retval << " " << parent.fstdout;
 4544    }
 4545    if (ch_N(parent.fstderr)) {
 4546        retval << " 2" << parent.fstderr;
 4547    }
 4548    return retval;
 4549}
 4550
 4551// --- command.acr_compl_proc.acr_compl.ToArgv
 4552// Form array from the command line
 4553void command::acr_compl_ToArgv(command::acr_compl_proc& parent, algo::StringAry& args) {
 4554    ary_RemoveAll(args);
 4555    ary_Alloc(args) << parent.path;
 4556
 4557    if (parent.cmd.data != "data") {
 4558        cstring *arg = &ary_Alloc(args);
 4559        *arg << "-data:";
 4560        cstring_Print(parent.cmd.data, *arg);
 4561    }
 4562
 4563    if (parent.cmd.schema != "data") {
 4564        cstring *arg = &ary_Alloc(args);
 4565        *arg << "-schema:";
 4566        cstring_Print(parent.cmd.schema, *arg);
 4567    }
 4568
 4569    if (parent.cmd.line != "") {
 4570        cstring *arg = &ary_Alloc(args);
 4571        *arg << "-line:";
 4572        cstring_Print(parent.cmd.line, *arg);
 4573    }
 4574
 4575    if (parent.cmd.point != "") {
 4576        cstring *arg = &ary_Alloc(args);
 4577        *arg << "-point:";
 4578        cstring_Print(parent.cmd.point, *arg);
 4579    }
 4580
 4581    if (parent.cmd.type != "9") {
 4582        cstring *arg = &ary_Alloc(args);
 4583        *arg << "-type:";
 4584        cstring_Print(parent.cmd.type, *arg);
 4585    }
 4586
 4587    if (parent.cmd.install != false) {
 4588        cstring *arg = &ary_Alloc(args);
 4589        *arg << "-install:";
 4590        bool_Print(parent.cmd.install, *arg);
 4591    }
 4592
 4593    if (parent.cmd.debug_log != "") {
 4594        cstring *arg = &ary_Alloc(args);
 4595        *arg << "-debug_log:";
 4596        cstring_Print(parent.cmd.debug_log, *arg);
 4597    }
 4598    for (int i=1; i < algo_lib::_db.cmdline.verbose; ++i) {
 4599        ary_Alloc(args) << "-verbose";
 4600    }
 4601}
 4602
 4603// --- command.acr_compl_proc..Uninit
 4604void command::acr_compl_proc_Uninit(command::acr_compl_proc& parent) {
 4605    command::acr_compl_proc &row = parent; (void)row;
 4606
 4607    // command.acr_compl_proc.acr_compl.Uninit (Exec)  //
 4608    acr_compl_Kill(parent); // kill child, ensure forward progress
 4609}
 4610
 4611// --- command.acr_dm.arg.Addary
 4612// Reserve space (this may move memory). Insert N element at the end.
 4613// Return aryptr to newly inserted block.
 4614// If the RHS argument aliases the array (refers to the same memory), exit program with fatal error.
 4615algo::aryptr<algo::cstring> command::arg_Addary(command::acr_dm& parent, algo::aryptr<algo::cstring> rhs) {
 4616    bool overlaps = rhs.n_elems>0 && rhs.elems >= parent.arg_elems && rhs.elems < parent.arg_elems + parent.arg_max;
 4617    if (UNLIKELY(overlaps)) {
 4618        FatalErrorExit("command.tary_alias  field:command.acr_dm.arg  comment:'alias error: sub-array is being appended to the whole'");
 4619    }
 4620    int nnew = rhs.n_elems;
 4621    arg_Reserve(parent, nnew); // reserve space
 4622    int at = parent.arg_n;
 4623    for (int i = 0; i < nnew; i++) {
 4624        new (parent.arg_elems + at + i) algo::cstring(rhs[i]);
 4625        parent.arg_n++;
 4626    }
 4627    return algo::aryptr<algo::cstring>(parent.arg_elems + at, nnew);
 4628}
 4629
 4630// --- command.acr_dm.arg.Alloc
 4631// Reserve space. Insert element at the end
 4632// The new element is initialized to a default value
 4633algo::cstring& command::arg_Alloc(command::acr_dm& parent) {
 4634    arg_Reserve(parent, 1);
 4635    int n  = parent.arg_n;
 4636    int at = n;
 4637    algo::cstring *elems = parent.arg_elems;
 4638    new (elems + at) algo::cstring(); // construct new element, default initializer
 4639    parent.arg_n = n+1;
 4640    return elems[at];
 4641}
 4642
 4643// --- command.acr_dm.arg.AllocAt
 4644// Reserve space for new element, reallocating the array if necessary
 4645// Insert new element at specified index. Index must be in range or a fatal error occurs.
 4646algo::cstring& command::arg_AllocAt(command::acr_dm& parent, int at) {
 4647    arg_Reserve(parent, 1);
 4648    int n  = parent.arg_n;
 4649    if (UNLIKELY(u64(at) >= u64(n+1))) {
 4650        FatalErrorExit("command.bad_alloc_at  field:command.acr_dm.arg  comment:'index out of range'");
 4651    }
 4652    algo::cstring *elems = parent.arg_elems;
 4653    memmove(elems + at + 1, elems + at, (n - at) * sizeof(algo::cstring));
 4654    new (elems + at) algo::cstring(); // construct element, default initializer
 4655    parent.arg_n = n+1;
 4656    return elems[at];
 4657}
 4658
 4659// --- command.acr_dm.arg.AllocN
 4660// Reserve space. Insert N elements at the end of the array, return pointer to array
 4661algo::aryptr<algo::cstring> command::arg_AllocN(command::acr_dm& parent, int n_elems) {
 4662    arg_Reserve(parent, n_elems);
 4663    int old_n  = parent.arg_n;
 4664    int new_n = old_n + n_elems;
 4665    algo::cstring *elems = parent.arg_elems;
 4666    for (int i = old_n; i < new_n; i++) {
 4667        new (elems + i) algo::cstring(); // construct new element, default initialize
 4668    }
 4669    parent.arg_n = new_n;
 4670    return algo::aryptr<algo::cstring>(elems + old_n, n_elems);
 4671}
 4672
 4673// --- command.acr_dm.arg.Remove
 4674// Remove item by index. If index outside of range, do nothing.
 4675void command::arg_Remove(command::acr_dm& parent, u32 i) {
 4676    u32 lim = parent.arg_n;
 4677    algo::cstring *elems = parent.arg_elems;
 4678    if (i < lim) {
 4679        elems[i].~cstring(); // destroy element
 4680        memmove(elems + i, elems + (i + 1), sizeof(algo::cstring) * (lim - (i + 1)));
 4681        parent.arg_n = lim - 1;
 4682    }
 4683}
 4684
 4685// --- command.acr_dm.arg.RemoveAll
 4686void command::arg_RemoveAll(command::acr_dm& parent) {
 4687    u32 n = parent.arg_n;
 4688    while (n > 0) {
 4689        n -= 1;
 4690        parent.arg_elems[n].~cstring();
 4691        parent.arg_n = n;
 4692    }
 4693}
 4694
 4695// --- command.acr_dm.arg.RemoveLast
 4696// Delete last element of array. Do nothing if array is empty.
 4697void command::arg_RemoveLast(command::acr_dm& parent) {
 4698    u64 n = parent.arg_n;
 4699    if (n > 0) {
 4700        n -= 1;
 4701        arg_qFind(parent, u64(n)).~cstring();
 4702        parent.arg_n = n;
 4703    }
 4704}
 4705
 4706// --- command.acr_dm.arg.AbsReserve
 4707// Make sure N elements fit in array. Process dies if out of memory
 4708void command::arg_AbsReserve(command::acr_dm& parent, int n) {
 4709    u32 old_max  = parent.arg_max;
 4710    if (n > i32(old_max)) {
 4711        u32 new_max  = i32_Max(i32_Max(old_max * 2, n), 4);
 4712        void *new_mem = algo_lib::malloc_ReallocMem(parent.arg_elems, old_max * sizeof(algo::cstring), new_max * sizeof(algo::cstring));
 4713        if (UNLIKELY(!new_mem)) {
 4714            FatalErrorExit("command.tary_nomem  field:command.acr_dm.arg  comment:'out of memory'");
 4715        }
 4716        parent.arg_elems = (algo::cstring*)new_mem;
 4717        parent.arg_max = new_max;
 4718    }
 4719}
 4720
 4721// --- command.acr_dm.arg.Setary
 4722// Copy contents of RHS to PARENT.
 4723void command::arg_Setary(command::acr_dm& parent, command::acr_dm &rhs) {
 4724    arg_RemoveAll(parent);
 4725    int nnew = rhs.arg_n;
 4726    arg_Reserve(parent, nnew); // reserve space
 4727    for (int i = 0; i < nnew; i++) { // copy elements over
 4728        new (parent.arg_elems + i) algo::cstring(arg_qFind(rhs, i));
 4729        parent.arg_n = i + 1;
 4730    }
 4731}
 4732
 4733// --- command.acr_dm.arg.Setary2
 4734// Copy specified array into arg, discarding previous contents.
 4735// If the RHS argument aliases the array (refers to the same memory), throw exception.
 4736void command::arg_Setary(command::acr_dm& parent, const algo::aryptr<algo::cstring> &rhs) {
 4737    arg_RemoveAll(parent);
 4738    arg_Addary(parent, rhs);
 4739}
 4740
 4741// --- command.acr_dm.arg.AllocNVal
 4742// Reserve space. Insert N elements at the end of the array, return pointer to array
 4743algo::aryptr<algo::cstring> command::arg_AllocNVal(command::acr_dm& parent, int n_elems, const algo::cstring& val) {
 4744    arg_Reserve(parent, n_elems);
 4745    int old_n  = parent.arg_n;
 4746    int new_n = old_n + n_elems;
 4747    algo::cstring *elems = parent.arg_elems;
 4748    for (int i = old_n; i < new_n; i++) {
 4749        new (elems + i) algo::cstring(val);
 4750    }
 4751    parent.arg_n = new_n;
 4752    return algo::aryptr<algo::cstring>(elems + old_n, n_elems);
 4753}
 4754
 4755// --- command.acr_dm.arg.ReadStrptrMaybe
 4756// A single element is read from input string and appended to the array.
 4757// If the string contains an error, the array is untouched.
 4758// Function returns success value.
 4759bool command::arg_ReadStrptrMaybe(command::acr_dm& parent, algo::strptr in_str) {
 4760    bool retval = true;
 4761    algo::cstring &elem = arg_Alloc(parent);
 4762    retval = algo::cstring_ReadStrptrMaybe(elem, in_str);
 4763    if (!retval) {
 4764        arg_RemoveLast(parent);
 4765    }
 4766    return retval;
 4767}
 4768
 4769// --- command.acr_dm..ReadFieldMaybe
 4770bool command::acr_dm_ReadFieldMaybe(command::acr_dm& parent, algo::strptr field, algo::strptr strval) {
 4771    bool retval = true;
 4772    command::FieldId field_id;
 4773    (void)value_SetStrptrMaybe(field_id,algo::Pathcomp(field, ".LL"));
 4774    switch(field_id) {
 4775        case command_FieldId_in: {
 4776            retval = algo::cstring_ReadStrptrMaybe(parent.in, strval);
 4777            break;
 4778        }
 4779        case command_FieldId_arg: {
 4780            retval = arg_ReadStrptrMaybe(parent, strval);
 4781            break;
 4782        }
 4783        case command_FieldId_write_ours: {
 4784            retval = bool_ReadStrptrMaybe(parent.write_ours, strval);
 4785            break;
 4786        }
 4787        case command_FieldId_msize: {
 4788            retval = u8_ReadStrptrMaybe(parent.msize, strval);
 4789            break;
 4790        }
 4791        case command_FieldId_rowid: {
 4792            retval = bool_ReadStrptrMaybe(parent.rowid, strval);
 4793            break;
 4794        }
 4795        default: break;
 4796    }
 4797    if (!retval) {
 4798        algo_lib::AppendErrtext("attr",field);
 4799    }
 4800    return retval;
 4801}
 4802
 4803// --- command.acr_dm..ReadTupleMaybe
 4804// Read fields of command::acr_dm from attributes of ascii tuple TUPLE
 4805bool command::acr_dm_ReadTupleMaybe(command::acr_dm &parent, algo::Tuple &tuple) {
 4806    bool retval = true;
 4807    int anon_idx = 0;
 4808    ind_beg(algo::Tuple_attrs_curs,attr,tuple) {
 4809        if (ch_N(attr.name) == 0) {
 4810            attr.name = acr_dm_GetAnon(parent, anon_idx++);
 4811        }
 4812        retval = acr_dm_ReadFieldMaybe(parent, attr.name, attr.value);
 4813        if (!retval) {
 4814            break;
 4815        }
 4816    }ind_end;
 4817    return retval;
 4818}
 4819
 4820// --- command.acr_dm..Uninit
 4821void command::acr_dm_Uninit(command::acr_dm& parent) {
 4822    command::acr_dm &row = parent; (void)row;
 4823
 4824    // command.acr_dm.arg.Uninit (Tary)  //Files to merge: older ours theirs...
 4825    // remove all elements from command.acr_dm.arg
 4826    arg_RemoveAll(parent);
 4827    // free memory for Tary command.acr_dm.arg
 4828    algo_lib::malloc_FreeMem(parent.arg_elems, sizeof(algo::cstring)*parent.arg_max); // (command.acr_dm.arg)
 4829}
 4830
 4831// --- command.acr_dm..ToCmdline
 4832// Convenience function that returns a full command line
 4833// Assume command is in a directory called bin
 4834tempstr command::acr_dm_ToCmdline(command::acr_dm& row) {
 4835    tempstr ret;
 4836    ret << "bin/acr_dm ";
 4837    acr_dm_PrintArgv(row, ret);
 4838    // inherit less intense verbose, debug options
 4839    for (int i = 1; i < algo_lib::_db.cmdline.verbose; i++) {
 4840        ret << " -verbose";
 4841    }
 4842    for (int i = 1; i < algo_lib::_db.cmdline.debug; i++) {
 4843        ret << " -debug";
 4844    }
 4845    return ret;
 4846}
 4847
 4848// --- command.acr_dm..PrintArgv
 4849// print string representation of ROW to string STR
 4850// cfmt:command.acr_dm.Argv  printfmt:Tuple
 4851void command::acr_dm_PrintArgv(command::acr_dm& row, algo::cstring& str) {
 4852    algo::tempstr temp;
 4853    (void)temp;
 4854    (void)str;
 4855    if (!(row.in == "data")) {
 4856        ch_RemoveAll(temp);
 4857        cstring_Print(row.in, temp);
 4858        str << " -in:";
 4859        strptr_PrintBash(temp,str);
 4860    }
 4861    ind_beg(acr_dm_arg_curs,value,row) {
 4862        ch_RemoveAll(temp);
 4863        cstring_Print(value, temp);
 4864        str << " -arg:";
 4865        strptr_PrintBash(temp,str);
 4866    }ind_end;
 4867    if (!(row.write_ours == false)) {
 4868        ch_RemoveAll(temp);
 4869        bool_Print(row.write_ours, temp);
 4870        str << " -write_ours:";
 4871        strptr_PrintBash(temp,str);
 4872    }
 4873    if (!(row.msize == 7)) {
 4874        ch_RemoveAll(temp);
 4875        u8_Print(row.msize, temp);
 4876        str << " -msize:";
 4877        strptr_PrintBash(temp,str);
 4878    }
 4879    if (!(row.rowid == false)) {
 4880        ch_RemoveAll(temp);
 4881        bool_Print(row.rowid, temp);
 4882        str << " -rowid:";
 4883        strptr_PrintBash(temp,str);
 4884    }
 4885}
 4886
 4887// --- command.acr_dm..GetAnon
 4888algo::strptr command::acr_dm_GetAnon(command::acr_dm &parent, i32 idx) {
 4889    (void)parent;//only to avoid -Wunused-parameter
 4890    switch(idx) {
 4891        default: return strptr("arg", 3);
 4892    }
 4893}
 4894
 4895// --- command.acr_dm..NArgs
 4896// Used with command lines
 4897// Return # of command-line arguments that must follow this argument
 4898// If FIELD is invalid, return -1
 4899i32 command::acr_dm_NArgs(command::FieldId field, algo::strptr& out_dflt, bool* out_anon) {
 4900    i32 retval = 1;
 4901    switch (field) {
 4902        case command_FieldId_in: { // $comment
 4903            *out_anon = false;
 4904        } break;
 4905        case command_FieldId_arg: { // $comment
 4906            *out_anon = true;
 4907        } break;
 4908        case command_FieldId_write_ours: { // $comment
 4909            *out_anon = false;
 4910            retval=0;
 4911            out_dflt="Y";
 4912        } break;
 4913        case command_FieldId_msize: { // bool: no argument required but value may be specified as write_ours:Y
 4914            *out_anon = false;
 4915        } break;
 4916        case command_FieldId_rowid: { // bool: no argument required but value may be specified as write_ours:Y
 4917            *out_anon = false;
 4918            retval=0;
 4919            out_dflt="Y";
 4920        } break;
 4921        default:
 4922        retval=-1; // unrecognized
 4923    }
 4924    return retval;
 4925}
 4926
 4927// --- command.acr_dm_proc.acr_dm.Start
 4928// Start subprocess
 4929// If subprocess already running, do nothing. Otherwise, start it
 4930int command::acr_dm_Start(command::acr_dm_proc& parent) {
 4931    int retval = 0;
 4932    if (parent.pid == 0) {
 4933        verblog(acr_dm_ToCmdline(parent)); // maybe print command
 4934#ifdef WIN32
 4935        algo_lib::ResolveExecFname(parent.path);
 4936        tempstr cmdline(acr_dm_ToCmdline(parent));
 4937        parent.pid = dospawn(Zeroterm(parent.path),Zeroterm(cmdline),parent.timeout,parent.fstdin,parent.fstdout,parent.fstderr);
 4938#else
 4939        parent.pid = fork();
 4940        if (parent.pid == 0) { // child
 4941            algo_lib::DieWithParent();
 4942            if (parent.timeout > 0) {
 4943                alarm(parent.timeout);
 4944            }
 4945            if (retval==0) retval=algo_lib::ApplyRedirect(parent.fstdin , 0);
 4946            if (retval==0) retval=algo_lib::ApplyRedirect(parent.fstdout, 1);
 4947            if (retval==0) retval=algo_lib::ApplyRedirect(parent.fstderr, 2);
 4948            if (retval==0) retval= acr_dm_Execv(parent);
 4949            if (retval != 0) { // if start fails, print error
 4950                int err=errno;
 4951                prerr("command.acr_dm_execv"
 4952                <<Keyval("errno",err)
 4953                <<Keyval("errstr",strerror(err))
 4954                <<Keyval("comment","Execv failed"));
 4955            }
 4956            _exit(127); // if failed to start, exit anyway
 4957        } else if (parent.pid == -1) {
 4958            retval = errno; // failed to fork
 4959        }
 4960#endif
 4961    }
 4962    parent.status = parent.pid > 0 ? 0 : -1; // if didn't start, set error status
 4963    return retval;
 4964}
 4965
 4966// --- command.acr_dm_proc.acr_dm.StartRead
 4967// Start subprocess & Read output
 4968algo::Fildes command::acr_dm_StartRead(command::acr_dm_proc& parent, algo_lib::FFildes &read) {
 4969    int pipefd[2];
 4970    int rc=pipe(pipefd);
 4971    (void)rc;
 4972    read.fd.value = pipefd[0];
 4973    parent.fstdout  << ">&" << pipefd[1];
 4974    acr_dm_Start(parent);
 4975    (void)close(pipefd[1]);
 4976    return read.fd;
 4977}
 4978
 4979// --- command.acr_dm_proc.acr_dm.Kill
 4980// Kill subprocess and wait
 4981void command::acr_dm_Kill(command::acr_dm_proc& parent) {
 4982    if (parent.pid != 0) {
 4983        kill(parent.pid,9);
 4984        acr_dm_Wait(parent);
 4985    }
 4986}
 4987
 4988// --- command.acr_dm_proc.acr_dm.Wait
 4989// Wait for subprocess to return
 4990void command::acr_dm_Wait(command::acr_dm_proc& parent) {
 4991    if (parent.pid > 0) {
 4992        int wait_flags = 0;
 4993        int wait_status = 0;
 4994        int rc = -1;
 4995        do {
 4996            // really wait for subprocess to exit
 4997            rc = waitpid(parent.pid,&wait_status,wait_flags);
 4998        } while (rc==-1 && errno==EINTR);
 4999        if (rc == parent.pid) {
 5000            parent.status = wait_status;
 5001            parent.pid = 0;
 5002        }
 5003    }
 5004}
 5005
 5006// --- command.acr_dm_proc.acr_dm.Exec
 5007// Start + Wait
 5008// Execute subprocess and return exit code
 5009int command::acr_dm_Exec(command::acr_dm_proc& parent) {
 5010    acr_dm_Start(parent);
 5011    acr_dm_Wait(parent);
 5012    return parent.status;
 5013}
 5014
 5015// --- command.acr_dm_proc.acr_dm.ExecX
 5016// Start + Wait, throw exception on error
 5017// Execute subprocess; throw human-readable exception on error
 5018void command::acr_dm_ExecX(command::acr_dm_proc& parent) {
 5019    int rc = acr_dm_Exec(parent);
 5020    vrfy(rc==0, tempstr() << "algo_lib.exec" << Keyval("cmd",acr_dm_ToCmdline(parent))
 5021    << Keyval("comment",algo::DescribeWaitStatus(parent.status)));
 5022}
 5023
 5024// --- command.acr_dm_proc.acr_dm.Execv
 5025// Call execv()
 5026// Call execv with specified parameters
 5027int command::acr_dm_Execv(command::acr_dm_proc& parent) {
 5028    int ret = 0;
 5029    algo::StringAry args;
 5030    acr_dm_ToArgv(parent, args);
 5031    char **argv = (char**)alloca((ary_N(args)+1)*sizeof(*argv));
 5032    ind_beg(algo::StringAry_ary_curs,arg,args) {
 5033        argv[ind_curs(arg).index] = Zeroterm(arg);
 5034    }ind_end;
 5035    argv[ary_N(args)] = NULL;
 5036    // if parent.path is relative, search for it in PATH
 5037    algo_lib::ResolveExecFname(parent.path);
 5038    ret = execv(Zeroterm(parent.path),argv);
 5039    return ret;
 5040}
 5041
 5042// --- command.acr_dm_proc.acr_dm.ToCmdline
 5043algo::tempstr command::acr_dm_ToCmdline(command::acr_dm_proc& parent) {
 5044    algo::tempstr retval;
 5045    retval << parent.path << " ";
 5046    command::acr_dm_PrintArgv(parent.cmd,retval);
 5047    if (ch_N(parent.fstdin)) {
 5048        retval << " " << parent.fstdin;
 5049    }
 5050    if (ch_N(parent.fstdout)) {
 5051        retval << " " << parent.fstdout;
 5052    }
 5053    if (ch_N(parent.fstderr)) {
 5054        retval << " 2" << parent.fstderr;
 5055    }
 5056    return retval;
 5057}
 5058
 5059// --- command.acr_dm_proc.acr_dm.ToArgv
 5060// Form array from the command line
 5061void command::acr_dm_ToArgv(command::acr_dm_proc& parent, algo::StringAry& args) {
 5062    ary_RemoveAll(args);
 5063    ary_Alloc(args) << parent.path;
 5064
 5065    if (parent.cmd.in != "data") {
 5066        cstring *arg = &ary_Alloc(args);
 5067        *arg << "-in:";
 5068        cstring_Print(parent.cmd.in, *arg);
 5069    }
 5070    ind_beg(command::acr_dm_arg_curs,value,parent.cmd) {
 5071        cstring *arg = &ary_Alloc(args);
 5072        *arg << "-arg:";
 5073        cstring_Print(value, *arg);
 5074    }ind_end;
 5075
 5076    if (parent.cmd.write_ours != false) {
 5077        cstring *arg = &ary_Alloc(args);
 5078        *arg << "-write_ours:";
 5079        bool_Print(parent.cmd.write_ours, *arg);
 5080    }
 5081
 5082    if (parent.cmd.msize != 7) {
 5083        cstring *arg = &ary_Alloc(args);
 5084        *arg << "-msize:";
 5085        u8_Print(parent.cmd.msize, *arg);
 5086    }
 5087
 5088    if (parent.cmd.rowid != false) {
 5089        cstring *arg = &ary_Alloc(args);
 5090        *arg << "-rowid:";
 5091        bool_Print(parent.cmd.rowid, *arg);
 5092    }
 5093    for (int i=1; i < algo_lib::_db.cmdline.verbose; ++i) {
 5094        ary_Alloc(args) << "-verbose";
 5095    }
 5096}
 5097
 5098// --- command.acr_dm_proc..Uninit
 5099void command::acr_dm_proc_Uninit(command::acr_dm_proc& parent) {
 5100    command::acr_dm_proc &row = parent; (void)row;
 5101
 5102    // command.acr_dm_proc.acr_dm.Uninit (Exec)  //
 5103    acr_dm_Kill(parent); // kill child, ensure forward progress
 5104}
 5105
 5106// --- command.acr_ed..ReadFieldMaybe
 5107bool command::acr_ed_ReadFieldMaybe(command::acr_ed& parent, algo::strptr field, algo::strptr strval) {
 5108    bool retval = true;
 5109    command::FieldId field_id;
 5110    (void)value_SetStrptrMaybe(field_id,field);
 5111    switch(field_id) {
 5112        case command_FieldId_in: {
 5113            retval = algo::cstring_ReadStrptrMaybe(parent.in, strval);
 5114            break;
 5115        }
 5116        case command_FieldId_create: {
 5117            retval = bool_ReadStrptrMaybe(parent.create, strval);
 5118            break;
 5119        }
 5120        case command_FieldId_del: {
 5121            retval = bool_ReadStrptrMaybe(parent.del, strval);
 5122            break;
 5123        }
 5124        case command_FieldId_rename: {
 5125            retval = algo::cstring_ReadStrptrMaybe(parent.rename, strval);
 5126            break;
 5127        }
 5128        case command_FieldId_finput: {
 5129            retval = bool_ReadStrptrMaybe(parent.finput, strval);
 5130            break;
 5131        }
 5132        case command_FieldId_foutput: {
 5133            retval = bool_ReadStrptrMaybe(parent.foutput, strval);
 5134            break;
 5135        }
 5136        case command_FieldId_srcfile: {
 5137            retval = algo::cstring_ReadStrptrMaybe(parent.srcfile, strval);
 5138            break;
 5139        }
 5140        case command_FieldId_gstatic: {
 5141            retval = bool_ReadStrptrMaybe(parent.gstatic, strval);
 5142            break;
 5143        }
 5144        case command_FieldId_indexed: {
 5145            retval = bool_ReadStrptrMaybe(parent.indexed, strval);
 5146            break;
 5147        }
 5148        case command_FieldId_target: {
 5149            retval = algo::Smallstr16_ReadStrptrMaybe(parent.target, strval);
 5150            break;
 5151        }
 5152        case command_FieldId_nstype: {
 5153            retval = algo::Smallstr50_ReadStrptrMaybe(parent.nstype, strval);
 5154            break;
 5155        }
 5156        case command_FieldId_ctype: {
 5157            retval = algo::Smallstr100_ReadStrptrMaybe(parent.ctype, strval);
 5158            break;
 5159        }
 5160        case command_FieldId_pooltype: {
 5161            retval = algo::Smallstr50_ReadStrptrMaybe(parent.pooltype, strval);
 5162            break;
 5163        }
 5164        case command_FieldId_ssimfile: {
 5165            retval = algo::Smallstr50_ReadStrptrMaybe(parent.ssimfile, strval);
 5166            break;
 5167        }
 5168        case command_FieldId_subset: {
 5169            retval = algo::Smallstr100_ReadStrptrMaybe(parent.subset, strval);
 5170            break;
 5171        }
 5172        case command_FieldId_subset2: {
 5173            retval = algo::Smallstr100_ReadStrptrMaybe(parent.subset2, strval);
 5174            break;
 5175        }
 5176        case command_FieldId_separator: {
 5177            retval = algo::cstring_ReadStrptrMaybe(parent.separator, strval);
 5178            break;
 5179        }
 5180        case command_FieldId_field: {
 5181            retval = algo::Smallstr100_ReadStrptrMaybe(parent.field, strval);
 5182            break;
 5183        }
 5184        case command_FieldId_arg: {
 5185            retval = algo::Smallstr100_ReadStrptrMaybe(parent.arg, strval);
 5186            break;
 5187        }
 5188        case command_FieldId_dflt: {
 5189            retval = algo::cstring_ReadStrptrMaybe(parent.dflt, strval);
 5190            break;
 5191        }
 5192        case command_FieldId_anon: {
 5193            retval = bool_ReadStrptrMaybe(parent.anon, strval);
 5194            break;
 5195        }
 5196        case command_FieldId_bigend: {
 5197            retval = bool_ReadStrptrMaybe(parent.bigend, strval);
 5198            break;
 5199        }
 5200        case command_FieldId_cascdel: {
 5201            retval = bool_ReadStrptrMaybe(parent.cascdel, strval);
 5202            break;
 5203        }
 5204        case command_FieldId_before: {
 5205            retval = algo::Smallstr100_ReadStrptrMaybe(parent.before, strval);
 5206            break;
 5207        }
 5208        case command_FieldId_substr: {
 5209            retval = algo::Smallstr100_ReadStrptrMaybe(parent.substr, strval);
 5210            break;
 5211        }
 5212        case command_FieldId_alias: {
 5213            retval = bool_ReadStrptrMaybe(parent.alias, strval);
 5214            break;
 5215        }
 5216        case command_FieldId_srcfield: {
 5217            retval = algo::Smallstr100_ReadStrptrMaybe(parent.srcfield, strval);
 5218            break;
 5219        }
 5220        case command_FieldId_fstep: {
 5221            retval = algo::Smallstr100_ReadStrptrMaybe(parent.fstep, strval);
 5222            break;
 5223        }
 5224        case command_FieldId_inscond: {
 5225            retval = algo::cstring_ReadStrptrMaybe(parent.inscond, strval);
 5226            break;
 5227        }
 5228        case command_FieldId_reftype: {
 5229            retval = algo::Smallstr50_ReadStrptrMaybe(parent.reftype, strval);
 5230            break;
 5231        }
 5232        case command_FieldId_hashfld: {
 5233            retval = algo::Smallstr100_ReadStrptrMaybe(parent.hashfld, strval);
 5234            break;
 5235        }
 5236        case command_FieldId_sortfld: {
 5237            retval = algo::Smallstr100_ReadStrptrMaybe(parent.sortfld, strval);
 5238            break;
 5239        }
 5240        case command_FieldId_unittest: {
 5241            retval = algo::cstring_ReadStrptrMaybe(parent.unittest, strval);
 5242            break;
 5243        }
 5244        case command_FieldId_citest: {
 5245            retval = algo::cstring_ReadStrptrMaybe(parent.citest, strval);
 5246            break;
 5247        }
 5248        case command_FieldId_cppfunc: {
 5249            retval = algo::cstring_ReadStrptrMaybe(parent.cppfunc, strval);
 5250            break;
 5251        }
 5252        case command_FieldId_xref: {
 5253            retval = bool_ReadStrptrMaybe(parent.xref, strval);
 5254            break;
 5255        }
 5256        case command_FieldId_via: {
 5257            retval = algo::cstring_ReadStrptrMaybe(parent.via, strval);
 5258            break;
 5259        }
 5260        case command_FieldId_write: {
 5261            retval = bool_ReadStrptrMaybe(parent.write, strval);
 5262            break;
 5263        }
 5264        case command_FieldId_e: {
 5265            retval = bool_ReadStrptrMaybe(parent.e, strval);
 5266            break;
 5267        }
 5268        case command_FieldId_comment: {
 5269            retval = algo::cstring_ReadStrptrMaybe(parent.comment, strval);
 5270            break;
 5271        }
 5272        case command_FieldId_sandbox: {
 5273            retval = bool_ReadStrptrMaybe(parent.sandbox, strval);
 5274            break;
 5275        }
 5276        case command_FieldId_test: {
 5277            retval = bool_ReadStrptrMaybe(parent.test, strval);
 5278            break;
 5279        }
 5280        case command_FieldId_showcpp: {
 5281            retval = bool_ReadStrptrMaybe(parent.showcpp, strval);
 5282            break;
 5283        }
 5284        case command_FieldId_msgtype: {
 5285            retval = algo::cstring_ReadStrptrMaybe(parent.msgtype, strval);
 5286            break;
 5287        }
 5288        case command_FieldId_anonfld: {
 5289            retval = bool_ReadStrptrMaybe(parent.anonfld, strval);
 5290            break;
 5291        }
 5292        default: break;
 5293    }
 5294    if (!retval) {
 5295        algo_lib::AppendErrtext("attr",field);
 5296    }
 5297    return retval;
 5298}
 5299
 5300// --- command.acr_ed..ReadTupleMaybe
 5301// Read fields of command::acr_ed from attributes of ascii tuple TUPLE
 5302bool command::acr_ed_ReadTupleMaybe(command::acr_ed &parent, algo::Tuple &tuple) {
 5303    bool retval = true;
 5304    ind_beg(algo::Tuple_attrs_curs,attr,tuple) {
 5305        retval = acr_ed_ReadFieldMaybe(parent, attr.name, attr.value);
 5306        if (!retval) {
 5307            break;
 5308        }
 5309    }ind_end;
 5310    return retval;
 5311}
 5312
 5313// --- command.acr_ed..Init
 5314// Set all fields to initial values.
 5315void command::acr_ed_Init(command::acr_ed& parent) {
 5316    parent.in = algo::strptr("data");
 5317    parent.create = bool(false);
 5318    parent.del = bool(false);
 5319    parent.rename = algo::strptr("");
 5320    parent.finput = bool(false);
 5321    parent.foutput = bool(false);
 5322    parent.srcfile = algo::strptr("");
 5323    parent.gstatic = bool(false);
 5324    parent.indexed = bool(false);
 5325    parent.target = algo::strptr("");
 5326    parent.nstype = algo::strptr("exe");
 5327    parent.ctype = algo::strptr("");
 5328    parent.pooltype = algo::strptr("");
 5329    parent.ssimfile = algo::strptr("");
 5330    parent.subset = algo::strptr("");
 5331    parent.subset2 = algo::strptr("");
 5332    parent.separator = algo::strptr(".");
 5333    parent.field = algo::strptr("");
 5334    parent.arg = algo::strptr("");
 5335    parent.dflt = algo::strptr("");
 5336    parent.anon = bool(false);
 5337    parent.bigend = bool(false);
 5338    parent.cascdel = bool(false);
 5339    parent.before = algo::strptr("");
 5340    parent.substr = algo::strptr("");
 5341    parent.alias = bool(false);
 5342    parent.srcfield = algo::strptr("");
 5343    parent.fstep = algo::strptr("");
 5344    parent.inscond = algo::strptr("true");
 5345    parent.reftype = algo::strptr("");
 5346    parent.hashfld = algo::strptr("");
 5347    parent.sortfld = algo::strptr("");
 5348    parent.unittest = algo::strptr("");
 5349    parent.citest = algo::strptr("");
 5350    parent.cppfunc = algo::strptr("");
 5351    parent.xref = bool(false);
 5352    parent.via = algo::strptr("");
 5353    parent.write = bool(false);
 5354    parent.e = bool(false);
 5355    parent.comment = algo::strptr("");
 5356    parent.sandbox = bool(false);
 5357    parent.test = bool(false);
 5358    parent.showcpp = bool(false);
 5359    parent.msgtype = algo::strptr("");
 5360    parent.anonfld = bool(false);
 5361}
 5362
 5363// --- command.acr_ed..ToCmdline
 5364// Convenience function that returns a full command line
 5365// Assume command is in a directory called bin
 5366tempstr command::acr_ed_ToCmdline(command::acr_ed& row) {
 5367    tempstr ret;
 5368    ret << "bin/acr_ed ";
 5369    acr_ed_PrintArgv(row, ret);
 5370    // inherit less intense verbose, debug options
 5371    for (int i = 1; i < algo_lib::_db.cmdline.verbose; i++) {
 5372        ret << " -verbose";
 5373    }
 5374    for (int i = 1; i < algo_lib::_db.cmdline.debug; i++) {
 5375        ret << " -debug";
 5376    }
 5377    return ret;
 5378}
 5379
 5380// --- command.acr_ed..PrintArgv
 5381// print string representation of ROW to string STR
 5382// cfmt:command.acr_ed.Argv  printfmt:Auto
 5383void command::acr_ed_PrintArgv(command::acr_ed& row, algo::cstring& str) {
 5384    algo::tempstr temp;
 5385    (void)temp;
 5386    (void)str;
 5387    if (!(row.in == "data")) {
 5388        ch_RemoveAll(temp);
 5389        cstring_Print(row.in, temp);
 5390        str << " -in:";
 5391        strptr_PrintBash(temp,str);
 5392    }
 5393    if (!(row.create == false)) {
 5394        ch_RemoveAll(temp);
 5395        bool_Print(row.create, temp);
 5396        str << " -create:";
 5397        strptr_PrintBash(temp,str);
 5398    }
 5399    if (!(row.del == false)) {
 5400        ch_RemoveAll(temp);
 5401        bool_Print(row.del, temp);
 5402        str << " -del:";
 5403        strptr_PrintBash(temp,str);
 5404    }
 5405    if (!(row.rename == "")) {
 5406        ch_RemoveAll(temp);
 5407        cstring_Print(row.rename, temp);
 5408        str << " -rename:";
 5409        strptr_PrintBash(temp,str);
 5410    }
 5411    if (!(row.finput == false)) {
 5412        ch_RemoveAll(temp);
 5413        bool_Print(row.finput, temp);
 5414        str << " -finput:";
 5415        strptr_PrintBash(temp,str);
 5416    }
 5417    if (!(row.foutput == false)) {
 5418        ch_RemoveAll(temp);
 5419        bool_Print(row.foutput, temp);
 5420        str << " -foutput:";
 5421        strptr_PrintBash(temp,str);
 5422    }
 5423    if (!(row.srcfile == "")) {
 5424        ch_RemoveAll(temp);
 5425        cstring_Print(row.srcfile, temp);
 5426        str << " -srcfile:";
 5427        strptr_PrintBash(temp,str);
 5428    }
 5429    if (!(row.gstatic == false)) {
 5430        ch_RemoveAll(temp);
 5431        bool_Print(row.gstatic, temp);
 5432        str << " -gstatic:";
 5433        strptr_PrintBash(temp,str);
 5434    }
 5435    if (!(row.indexed == false)) {
 5436        ch_RemoveAll(temp);
 5437        bool_Print(row.indexed, temp);
 5438        str << " -indexed:";
 5439        strptr_PrintBash(temp,str);
 5440    }
 5441    if (!(row.target == "")) {
 5442        ch_RemoveAll(temp);
 5443        Smallstr16_Print(row.target, temp);
 5444        str << " -target:";
 5445        strptr_PrintBash(temp,str);
 5446    }
 5447    if (!(row.nstype == "exe")) {
 5448        ch_RemoveAll(temp);
 5449        Smallstr50_Print(row.nstype, temp);
 5450        str << " -nstype:";
 5451        strptr_PrintBash(temp,str);
 5452    }
 5453    if (!(row.ctype == "")) {
 5454        ch_RemoveAll(temp);
 5455        Smallstr100_Print(row.ctype, temp);
 5456        str << " -ctype:";
 5457        strptr_PrintBash(temp,str);
 5458    }
 5459    if (!(row.pooltype == "")) {
 5460        ch_RemoveAll(temp);
 5461        Smallstr50_Print(row.pooltype, temp);
 5462        str << " -pooltype:";
 5463        strptr_PrintBash(temp,str);
 5464    }
 5465    if (!(row.ssimfile == "")) {
 5466        ch_RemoveAll(temp);
 5467        Smallstr50_Print(row.ssimfile, temp);
 5468        str << " -ssimfile:";
 5469        strptr_PrintBash(temp,str);
 5470    }
 5471    if (!(row.subset == "")) {
 5472        ch_RemoveAll(temp);
 5473        Smallstr100_Print(row.subset, temp);
 5474        str << " -subset:";
 5475        strptr_PrintBash(temp,str);
 5476    }
 5477    if (!(row.subset2 == "")) {
 5478        ch_RemoveAll(temp);
 5479        Smallstr100_Print(row.subset2, temp);
 5480        str << " -subset2:";
 5481        strptr_PrintBash(temp,str);
 5482    }
 5483    if (!(row.separator == ".")) {
 5484        ch_RemoveAll(temp);
 5485        cstring_Print(row.separator, temp);
 5486        str << " -separator:";
 5487        strptr_PrintBash(temp,str);
 5488    }
 5489    if (!(row.field == "")) {
 5490        ch_RemoveAll(temp);
 5491        Smallstr100_Print(row.field, temp);
 5492        str << " -field:";
 5493        strptr_PrintBash(temp,str);
 5494    }
 5495    if (!(row.arg == "")) {
 5496        ch_RemoveAll(temp);
 5497        Smallstr100_Print(row.arg, temp);
 5498        str << " -arg:";
 5499        strptr_PrintBash(temp,str);
 5500    }
 5501    if (!(row.dflt == "")) {
 5502        ch_RemoveAll(temp);
 5503        cstring_Print(row.dflt, temp);
 5504        str << " -dflt:";
 5505        strptr_PrintBash(temp,str);
 5506    }
 5507    if (!(row.anon == false)) {
 5508        ch_RemoveAll(temp);
 5509        bool_Print(row.anon, temp);
 5510        str << " -anon:";
 5511        strptr_PrintBash(temp,str);
 5512    }
 5513    if (!(row.bigend == false)) {
 5514        ch_RemoveAll(temp);
 5515        bool_Print(row.bigend, temp);
 5516        str << " -bigend:";
 5517        strptr_PrintBash(temp,str);
 5518    }
 5519    if (!(row.cascdel == false)) {
 5520        ch_RemoveAll(temp);
 5521        bool_Print(row.cascdel, temp);
 5522        str << " -cascdel:";
 5523        strptr_PrintBash(temp,str);
 5524    }
 5525    if (!(row.before == "")) {
 5526        ch_RemoveAll(temp);
 5527        Smallstr100_Print(row.before, temp);
 5528        str << " -before:";
 5529        strptr_PrintBash(temp,str);
 5530    }
 5531    if (!(row.substr == "")) {
 5532        ch_RemoveAll(temp);
 5533        Smallstr100_Print(row.substr, temp);
 5534        str << " -substr:";
 5535        strptr_PrintBash(temp,str);
 5536    }
 5537    if (!(row.alias == false)) {
 5538        ch_RemoveAll(temp);
 5539        bool_Print(row.alias, temp);
 5540        str << " -alias:";
 5541        strptr_PrintBash(temp,str);
 5542    }
 5543    if (!(row.srcfield == "")) {
 5544        ch_RemoveAll(temp);
 5545        Smallstr100_Print(row.srcfield, temp);
 5546        str << " -srcfield:";
 5547        strptr_PrintBash(temp,str);
 5548    }
 5549    if (!(row.fstep == "")) {
 5550        ch_RemoveAll(temp);
 5551        Smallstr100_Print(row.fstep, temp);
 5552        str << " -fstep:";
 5553        strptr_PrintBash(temp,str);
 5554    }
 5555    if (!(row.inscond == "true")) {
 5556        ch_RemoveAll(temp);
 5557        cstring_Print(row.inscond, temp);
 5558        str << " -inscond:";
 5559        strptr_PrintBash(temp,str);
 5560    }
 5561    if (!(row.reftype == "")) {
 5562        ch_RemoveAll(temp);
 5563        Smallstr50_Print(row.reftype, temp);
 5564        str << " -reftype:";
 5565        strptr_PrintBash(temp,str);
 5566    }
 5567    if (!(row.hashfld == "")) {
 5568        ch_RemoveAll(temp);
 5569        Smallstr100_Print(row.hashfld, temp);
 5570        str << " -hashfld:";
 5571        strptr_PrintBash(temp,str);
 5572    }
 5573    if (!(row.sortfld == "")) {
 5574        ch_RemoveAll(temp);
 5575        Smallstr100_Print(row.sortfld, temp);
 5576        str << " -sortfld:";
 5577        strptr_PrintBash(temp,str);
 5578    }
 5579    if (!(row.unittest == "")) {
 5580        ch_RemoveAll(temp);
 5581        cstring_Print(row.unittest, temp);
 5582        str << " -unittest:";
 5583        strptr_PrintBash(temp,str);
 5584    }
 5585    if (!(row.citest == "")) {
 5586        ch_RemoveAll(temp);
 5587        cstring_Print(row.citest, temp);
 5588        str << " -citest:";
 5589        strptr_PrintBash(temp,str);
 5590    }
 5591    if (!(row.cppfunc == "")) {
 5592        ch_RemoveAll(temp);
 5593        cstring_Print(row.cppfunc, temp);
 5594        str << " -cppfunc:";
 5595        strptr_PrintBash(temp,str);
 5596    }
 5597    if (!(row.xref == false)) {
 5598        ch_RemoveAll(temp);
 5599        bool_Print(row.xref, temp);
 5600        str << " -xref:";
 5601        strptr_PrintBash(temp,str);
 5602    }
 5603    if (!(row.via == "")) {
 5604        ch_RemoveAll(temp);
 5605        cstring_Print(row.via, temp);
 5606        str << " -via:";
 5607        strptr_PrintBash(temp,str);
 5608    }
 5609    if (!(row.write == false)) {
 5610        ch_RemoveAll(temp);
 5611        bool_Print(row.write, temp);
 5612        str << " -write:";
 5613        strptr_PrintBash(temp,str);
 5614    }
 5615    if (!(row.e == false)) {
 5616        ch_RemoveAll(temp);
 5617        bool_Print(row.e, temp);
 5618        str << " -e:";
 5619        strptr_PrintBash(temp,str);
 5620    }
 5621    if (!(row.comment == "")) {
 5622        ch_RemoveAll(temp);
 5623        cstring_Print(row.comment, temp);
 5624        str << " -comment:";
 5625        strptr_PrintBash(temp,str);
 5626    }
 5627    if (!(row.sandbox == false)) {
 5628        ch_RemoveAll(temp);
 5629        bool_Print(row.sandbox, temp);
 5630        str << " -sandbox:";
 5631        strptr_PrintBash(temp,str);
 5632    }
 5633    if (!(row.test == false)) {
 5634        ch_RemoveAll(temp);
 5635        bool_Print(row.test, temp);
 5636        str << " -test:";
 5637        strptr_PrintBash(temp,str);
 5638    }
 5639    if (!(row.showcpp == false)) {
 5640        ch_RemoveAll(temp);
 5641        bool_Print(row.showcpp, temp);
 5642        str << " -showcpp:";
 5643        strptr_PrintBash(temp,str);
 5644    }
 5645    if (!(row.msgtype == "")) {
 5646        ch_RemoveAll(temp);
 5647        cstring_Print(row.msgtype, temp);
 5648        str << " -msgtype:";
 5649        strptr_PrintBash(temp,str);
 5650    }
 5651    if (!(row.anonfld == false)) {
 5652        ch_RemoveAll(temp);
 5653        bool_Print(row.anonfld, temp);
 5654        str << " -anonfld:";
 5655        strptr_PrintBash(temp,str);
 5656    }
 5657}
 5658
 5659// --- command.acr_ed..NArgs
 5660// Used with command lines
 5661// Return # of command-line arguments that must follow this argument
 5662// If FIELD is invalid, return -1
 5663i32 command::acr_ed_NArgs(command::FieldId field, algo::strptr& out_dflt, bool* out_anon) {
 5664    i32 retval = 1;
 5665    switch (field) {
 5666        case command_FieldId_in: { // $comment
 5667            *out_anon = false;
 5668        } break;
 5669        case command_FieldId_create: { // $comment
 5670            *out_anon = false;
 5671            retval=0;
 5672            out_dflt="Y";
 5673        } break;
 5674        case command_FieldId_del: { // bool: no argument required but value may be specified as create:Y
 5675            *out_anon = false;
 5676            retval=0;
 5677            out_dflt="Y";
 5678        } break;
 5679        case command_FieldId_rename: { // bool: no argument required but value may be specified as del:Y
 5680            *out_anon = false;
 5681        } break;
 5682        case command_FieldId_finput: { // bool: no argument required but value may be specified as del:Y
 5683            *out_anon = false;
 5684            retval=0;
 5685            out_dflt="Y";
 5686        } break;
 5687        case command_FieldId_foutput: { // bool: no argument required but value may be specified as finput:Y
 5688            *out_anon = false;
 5689            retval=0;
 5690            out_dflt="Y";
 5691        } break;
 5692        case command_FieldId_srcfile: { // bool: no argument required but value may be specified as foutput:Y
 5693            *out_anon = false;
 5694        } break;
 5695        case command_FieldId_gstatic: { // bool: no argument required but value may be specified as foutput:Y
 5696            *out_anon = false;
 5697            retval=0;
 5698            out_dflt="Y";
 5699        } break;
 5700        case command_FieldId_indexed: { // bool: no argument required but value may be specified as gstatic:Y
 5701            *out_anon = false;
 5702            retval=0;
 5703            out_dflt="Y";
 5704        } break;
 5705        case command_FieldId_target: { // bool: no argument required but value may be specified as indexed:Y
 5706            *out_anon = false;
 5707        } break;
 5708        case command_FieldId_nstype: { // bool: no argument required but value may be specified as indexed:Y
 5709            *out_anon = false;
 5710        } break;
 5711        case command_FieldId_ctype: { // bool: no argument required but value may be specified as indexed:Y
 5712            *out_anon = false;
 5713        } break;
 5714        case command_FieldId_pooltype: { // bool: no argument required but value may be specified as indexed:Y
 5715            *out_anon = false;
 5716        } break;
 5717        case command_FieldId_ssimfile: { // bool: no argument required but value may be specified as indexed:Y
 5718            *out_anon = false;
 5719        } break;
 5720        case command_FieldId_subset: { // bool: no argument required but value may be specified as indexed:Y
 5721            *out_anon = false;
 5722        } break;
 5723        case command_FieldId_subset2: { // bool: no argument required but value may be specified as indexed:Y
 5724            *out_anon = false;
 5725        } break;
 5726        case command_FieldId_separator: { // bool: no argument required but value may be specified as indexed:Y
 5727            *out_anon = false;
 5728        } break;
 5729        case command_FieldId_field: { // bool: no argument required but value may be specified as indexed:Y
 5730            *out_anon = false;
 5731        } break;
 5732        case command_FieldId_arg: { // bool: no argument required but value may be specified as indexed:Y
 5733            *out_anon = false;
 5734        } break;
 5735        case command_FieldId_dflt: { // bool: no argument required but value may be specified as indexed:Y
 5736            *out_anon = false;
 5737        } break;
 5738        case command_FieldId_anon: { // bool: no argument required but value may be specified as indexed:Y
 5739            *out_anon = false;
 5740            retval=0;
 5741            out_dflt="Y";
 5742        } break;
 5743        case command_FieldId_bigend: { // bool: no argument required but value may be specified as anon:Y
 5744            *out_anon = false;
 5745            retval=0;
 5746            out_dflt="Y";
 5747        } break;
 5748        case command_FieldId_cascdel: { // bool: no argument required but value may be specified as bigend:Y
 5749            *out_anon = false;
 5750            retval=0;
 5751            out_dflt="Y";
 5752        } break;
 5753        case command_FieldId_before: { // bool: no argument required but value may be specified as cascdel:Y
 5754            *out_anon = false;
 5755        } break;
 5756        case command_FieldId_substr: { // bool: no argument required but value may be specified as cascdel:Y
 5757            *out_anon = false;
 5758        } break;
 5759        case command_FieldId_alias: { // bool: no argument required but value may be specified as cascdel:Y
 5760            *out_anon = false;
 5761            retval=0;
 5762            out_dflt="Y";
 5763        } break;
 5764        case command_FieldId_srcfield: { // bool: no argument required but value may be specified as alias:Y
 5765            *out_anon = false;
 5766        } break;
 5767        case command_FieldId_fstep: { // bool: no argument required but value may be specified as alias:Y
 5768            *out_anon = false;
 5769        } break;
 5770        case command_FieldId_inscond: { // bool: no argument required but value may be specified as alias:Y
 5771            *out_anon = false;
 5772        } break;
 5773        case command_FieldId_reftype: { // bool: no argument required but value may be specified as alias:Y
 5774            *out_anon = false;
 5775        } break;
 5776        case command_FieldId_hashfld: { // bool: no argument required but value may be specified as alias:Y
 5777            *out_anon = false;
 5778        } break;
 5779        case command_FieldId_sortfld: { // bool: no argument required but value may be specified as alias:Y
 5780            *out_anon = false;
 5781        } break;
 5782        case command_FieldId_unittest: { // bool: no argument required but value may be specified as alias:Y
 5783            *out_anon = false;
 5784        } break;
 5785        case command_FieldId_citest: { // bool: no argument required but value may be specified as alias:Y
 5786            *out_anon = false;
 5787        } break;
 5788        case command_FieldId_cppfunc: { // bool: no argument required but value may be specified as alias:Y
 5789            *out_anon = false;
 5790        } break;
 5791        case command_FieldId_xref: { // bool: no argument required but value may be specified as alias:Y
 5792            *out_anon = false;
 5793            retval=0;
 5794            out_dflt="Y";
 5795        } break;
 5796        case command_FieldId_via: { // bool: no argument required but value may be specified as xref:Y
 5797            *out_anon = false;
 5798        } break;
 5799        case command_FieldId_write: { // bool: no argument required but value may be specified as xref:Y
 5800            *out_anon = false;
 5801            retval=0;
 5802            out_dflt="Y";
 5803        } break;
 5804        case command_FieldId_e: { // bool: no argument required but value may be specified as write:Y
 5805            *out_anon = false;
 5806            retval=0;
 5807            out_dflt="Y";
 5808        } break;
 5809        case command_FieldId_comment: { // bool: no argument required but value may be specified as e:Y
 5810            *out_anon = false;
 5811        } break;
 5812        case command_FieldId_sandbox: { // bool: no argument required but value may be specified as e:Y
 5813            *out_anon = false;
 5814            retval=0;
 5815            out_dflt="Y";
 5816        } break;
 5817        case command_FieldId_test: { // bool: no argument required but value may be specified as sandbox:Y
 5818            *out_anon = false;
 5819            retval=0;
 5820            out_dflt="Y";
 5821        } break;
 5822        case command_FieldId_showcpp: { // bool: no argument required but value may be specified as test:Y
 5823            *out_anon = false;
 5824            retval=0;
 5825            out_dflt="Y";
 5826        } break;
 5827        case command_FieldId_msgtype: { // bool: no argument required but value may be specified as showcpp:Y
 5828            *out_anon = false;
 5829        } break;
 5830        case command_FieldId_anonfld: { // bool: no argument required but value may be specified as showcpp:Y
 5831            *out_anon = false;
 5832            retval=0;
 5833            out_dflt="Y";
 5834        } break;
 5835        default:
 5836        retval=-1; // unrecognized
 5837    }
 5838    return retval;
 5839}
 5840
 5841// --- command.acr_ed_proc.acr_ed.Start
 5842// Start subprocess
 5843// If subprocess already running, do nothing. Otherwise, start it
 5844int command::acr_ed_Start(command::acr_ed_proc& parent) {
 5845    int retval = 0;
 5846    if (parent.pid == 0) {
 5847        verblog(acr_ed_ToCmdline(parent)); // maybe print command
 5848#ifdef WIN32
 5849        algo_lib::ResolveExecFname(parent.path);
 5850        tempstr cmdline(acr_ed_ToCmdline(parent));
 5851        parent.pid = dospawn(Zeroterm(parent.path),Zeroterm(cmdline),parent.timeout,parent.fstdin,parent.fstdout,parent.fstderr);
 5852#else
 5853        parent.pid = fork();
 5854        if (parent.pid == 0) { // child
 5855            algo_lib::DieWithParent();
 5856            if (parent.timeout > 0) {
 5857                alarm(parent.timeout);
 5858            }
 5859            if (retval==0) retval=algo_lib::ApplyRedirect(parent.fstdin , 0);
 5860            if (retval==0) retval=algo_lib::ApplyRedirect(parent.fstdout, 1);
 5861            if (retval==0) retval=algo_lib::ApplyRedirect(parent.fstderr, 2);
 5862            if (retval==0) retval= acr_ed_Execv(parent);
 5863            if (retval != 0) { // if start fails, print error
 5864                int err=errno;
 5865                prerr("command.acr_ed_execv"
 5866                <<Keyval("errno",err)
 5867                <<Keyval("errstr",strerror(err))
 5868                <<Keyval("comment","Execv failed"));
 5869            }
 5870            _exit(127); // if failed to start, exit anyway
 5871        } else if (parent.pid == -1) {
 5872            retval = errno; // failed to fork
 5873        }
 5874#endif
 5875    }
 5876    parent.status = parent.pid > 0 ? 0 : -1; // if didn't start, set error status
 5877    return retval;
 5878}
 5879
 5880// --- command.acr_ed_proc.acr_ed.StartRead
 5881// Start subprocess & Read output
 5882algo::Fildes command::acr_ed_StartRead(command::acr_ed_proc& parent, algo_lib::FFildes &read) {
 5883    int pipefd[2];
 5884    int rc=pipe(pipefd);
 5885    (void)rc;
 5886    read.fd.value = pipefd[0];
 5887    parent.fstdout  << ">&" << pipefd[1];
 5888    acr_ed_Start(parent);
 5889    (void)close(pipefd[1]);
 5890    return read.fd;
 5891}
 5892
 5893// --- command.acr_ed_proc.acr_ed.Kill
 5894// Kill subprocess and wait
 5895void command::acr_ed_Kill(command::acr_ed_proc& parent) {
 5896    if (parent.pid != 0) {
 5897        kill(parent.pid,9);
 5898        acr_ed_Wait(parent);
 5899    }
 5900}
 5901
 5902// --- command.acr_ed_proc.acr_ed.Wait
 5903// Wait for subprocess to return
 5904void command::acr_ed_Wait(command::acr_ed_proc& parent) {
 5905    if (parent.pid > 0) {
 5906        int wait_flags = 0;
 5907        int wait_status = 0;
 5908        int rc = -1;
 5909        do {
 5910            // really wait for subprocess to exit
 5911            rc = waitpid(parent.pid,&wait_status,wait_flags);
 5912        } while (rc==-1 && errno==EINTR);
 5913        if (rc == parent.pid) {
 5914            parent.status = wait_status;
 5915            parent.pid = 0;
 5916        }
 5917    }
 5918}
 5919
 5920// --- command.acr_ed_proc.acr_ed.Exec
 5921// Start + Wait
 5922// Execute subprocess and return exit code
 5923int command::acr_ed_Exec(command::acr_ed_proc& parent) {
 5924    acr_ed_Start(parent);
 5925    acr_ed_Wait(parent);
 5926    return parent.status;
 5927}
 5928
 5929// --- command.acr_ed_proc.acr_ed.ExecX
 5930// Start + Wait, throw exception on error
 5931// Execute subprocess; throw human-readable exception on error
 5932void command::acr_ed_ExecX(command::acr_ed_proc& parent) {
 5933    int rc = acr_ed_Exec(parent);
 5934    vrfy(rc==0, tempstr() << "algo_lib.exec" << Keyval("cmd",acr_ed_ToCmdline(parent))
 5935    << Keyval("comment",algo::DescribeWaitStatus(parent.status)));
 5936}
 5937
 5938// --- command.acr_ed_proc.acr_ed.Execv
 5939// Call execv()
 5940// Call execv with specified parameters
 5941int command::acr_ed_Execv(command::acr_ed_proc& parent) {
 5942    int ret = 0;
 5943    algo::StringAry args;
 5944    acr_ed_ToArgv(parent, args);
 5945    char **argv = (char**)alloca((ary_N(args)+1)*sizeof(*argv));
 5946    ind_beg(algo::StringAry_ary_curs,arg,args) {
 5947        argv[ind_curs(arg).index] = Zeroterm(arg);
 5948    }ind_end;
 5949    argv[ary_N(args)] = NULL;
 5950    // if parent.path is relative, search for it in PATH
 5951    algo_lib::ResolveExecFname(parent.path);
 5952    ret = execv(Zeroterm(parent.path),argv);
 5953    return ret;
 5954}
 5955
 5956// --- command.acr_ed_proc.acr_ed.ToCmdline
 5957algo::tempstr command::acr_ed_ToCmdline(command::acr_ed_proc& parent) {
 5958    algo::tempstr retval;
 5959    retval << parent.path << " ";
 5960    command::acr_ed_PrintArgv(parent.cmd,retval);
 5961    if (ch_N(parent.fstdin)) {
 5962        retval << " " << parent.fstdin;
 5963    }
 5964    if (ch_N(parent.fstdout)) {
 5965        retval << " " << parent.fstdout;
 5966    }
 5967    if (ch_N(parent.fstderr)) {
 5968        retval << " 2" << parent.fstderr;
 5969    }
 5970    return retval;
 5971}
 5972
 5973// --- command.acr_ed_proc.acr_ed.ToArgv
 5974// Form array from the command line
 5975void command::acr_ed_ToArgv(command::acr_ed_proc& parent, algo::StringAry& args) {
 5976    ary_RemoveAll(args);
 5977    ary_Alloc(args) << parent.path;
 5978
 5979    if (parent.cmd.in != "data") {
 5980        cstring *arg = &ary_Alloc(args);
 5981        *arg << "-in:";
 5982        cstring_Print(parent.cmd.in, *arg);
 5983    }
 5984
 5985    if (parent.cmd.create != false) {
 5986        cstring *arg = &ary_Alloc(args);
 5987        *arg << "-create:";
 5988        bool_Print(parent.cmd.create, *arg);
 5989    }
 5990
 5991    if (parent.cmd.del != false) {
 5992        cstring *arg = &ary_Alloc(args);
 5993        *arg << "-del:";
 5994        bool_Print(parent.cmd.del, *arg);
 5995    }
 5996
 5997    if (parent.cmd.rename != "") {
 5998        cstring *arg = &ary_Alloc(args);
 5999        *arg << "-rename:";
 6000        cstring_Print(parent.cmd.rename, *arg);
 6001    }
 6002
 6003    if (parent.cmd.finput != false) {
 6004        cstring *arg = &ary_Alloc(args);
 6005        *arg << "-finput:";
 6006        bool_Print(parent.cmd.finput, *arg);
 6007    }
 6008
 6009    if (parent.cmd.foutput != false) {
 6010        cstring *arg = &ary_Alloc(args);
 6011        *arg << "-foutput:";
 6012        bool_Print(parent.cmd.foutput, *arg);
 6013    }
 6014
 6015    if (parent.cmd.srcfile != "") {
 6016        cstring *arg = &ary_Alloc(args);
 6017        *arg << "-srcfile:";
 6018        cstring_Print(parent.cmd.srcfile, *arg);
 6019    }
 6020
 6021    if (parent.cmd.gstatic != false) {
 6022        cstring *arg = &ary_Alloc(args);
 6023        *arg << "-gstatic:";
 6024        bool_Print(parent.cmd.gstatic, *arg);
 6025    }
 6026
 6027    if (parent.cmd.indexed != false) {
 6028        cstring *arg = &ary_Alloc(args);
 6029        *arg << "-indexed:";
 6030        bool_Print(parent.cmd.indexed, *arg);
 6031    }
 6032
 6033    if (parent.cmd.target != "") {
 6034        cstring *arg = &ary_Alloc(args);
 6035        *arg << "-target:";
 6036        Smallstr16_Print(parent.cmd.target, *arg);
 6037    }
 6038
 6039    if (parent.cmd.nstype != "exe") {
 6040        cstring *arg = &ary_Alloc(args);
 6041        *arg << "-nstype:";
 6042        Smallstr50_Print(parent.cmd.nstype, *arg);
 6043    }
 6044
 6045    if (parent.cmd.ctype != "") {
 6046        cstring *arg = &ary_Alloc(args);
 6047        *arg << "-ctype:";
 6048        Smallstr100_Print(parent.cmd.ctype, *arg);
 6049    }
 6050
 6051    if (parent.cmd.pooltype != "") {
 6052        cstring *arg = &ary_Alloc(args);
 6053        *arg << "-pooltype:";
 6054        Smallstr50_Print(parent.cmd.pooltype, *arg);
 6055    }
 6056
 6057    if (parent.cmd.ssimfile != "") {
 6058        cstring *arg = &ary_Alloc(args);
 6059        *arg << "-ssimfile:";
 6060        Smallstr50_Print(parent.cmd.ssimfile, *arg);
 6061    }
 6062
 6063    if (parent.cmd.subset != "") {
 6064        cstring *arg = &ary_Alloc(args);
 6065        *arg << "-subset:";
 6066        Smallstr100_Print(parent.cmd.subset, *arg);
 6067    }
 6068
 6069    if (parent.cmd.subset2 != "") {
 6070        cstring *arg = &ary_Alloc(args);
 6071        *arg << "-subset2:";
 6072        Smallstr100_Print(parent.cmd.subset2, *arg);
 6073    }
 6074
 6075    if (parent.cmd.separator != ".") {
 6076        cstring *arg = &ary_Alloc(args);
 6077        *arg << "-separator:";
 6078        cstring_Print(parent.cmd.separator, *arg);
 6079    }
 6080
 6081    if (parent.cmd.field != "") {
 6082        cstring *arg = &ary_Alloc(args);
 6083        *arg << "-field:";
 6084        Smallstr100_Print(parent.cmd.field, *arg);
 6085    }
 6086
 6087    if (parent.cmd.arg != "") {
 6088        cstring *arg = &ary_Alloc(args);
 6089        *arg << "-arg:";
 6090        Smallstr100_Print(parent.cmd.arg, *arg);
 6091    }
 6092
 6093    if (parent.cmd.dflt != "") {
 6094        cstring *arg = &ary_Alloc(args);
 6095        *arg << "-dflt:";
 6096        cstring_Print(parent.cmd.dflt, *arg);
 6097    }
 6098
 6099    if (parent.cmd.anon != false) {
 6100        cstring *arg = &ary_Alloc(args);
 6101        *arg << "-anon:";
 6102        bool_Print(parent.cmd.anon, *arg);
 6103    }
 6104
 6105    if (parent.cmd.bigend != false) {
 6106        cstring *arg = &ary_Alloc(args);
 6107        *arg << "-bigend:";
 6108        bool_Print(parent.cmd.bigend, *arg);
 6109    }
 6110
 6111    if (parent.cmd.cascdel != false) {
 6112        cstring *arg = &ary_Alloc(args);
 6113        *arg << "-cascdel:";
 6114        bool_Print(parent.cmd.cascdel, *arg);
 6115    }
 6116
 6117    if (parent.cmd.before != "") {
 6118        cstring *arg = &ary_Alloc(args);
 6119        *arg << "-before:";
 6120        Smallstr100_Print(parent.cmd.before, *arg);
 6121    }
 6122
 6123    if (parent.cmd.substr != "") {
 6124        cstring *arg = &ary_Alloc(args);
 6125        *arg << "-substr:";
 6126        Smallstr100_Print(parent.cmd.substr, *arg);
 6127    }
 6128
 6129    if (parent.cmd.alias != false) {
 6130        cstring *arg = &ary_Alloc(args);
 6131        *arg << "-alias:";
 6132        bool_Print(parent.cmd.alias, *arg);
 6133    }
 6134
 6135    if (parent.cmd.srcfield != "") {
 6136        cstring *arg = &ary_Alloc(args);
 6137        *arg << "-srcfield:";
 6138        Smallstr100_Print(parent.cmd.srcfield, *arg);
 6139    }
 6140
 6141    if (parent.cmd.fstep != "") {
 6142        cstring *arg = &ary_Alloc(args);
 6143        *arg << "-fstep:";
 6144        Smallstr100_Print(parent.cmd.fstep, *arg);
 6145    }
 6146
 6147    if (parent.cmd.inscond != "true") {
 6148        cstring *arg = &ary_Alloc(args);
 6149        *arg << "-inscond:";
 6150        cstring_Print(parent.cmd.inscond, *arg);
 6151    }
 6152
 6153    if (parent.cmd.reftype != "") {
 6154        cstring *arg = &ary_Alloc(args);
 6155        *arg << "-reftype:";
 6156        Smallstr50_Print(parent.cmd.reftype, *arg);
 6157    }
 6158
 6159    if (parent.cmd.hashfld != "") {
 6160        cstring *arg = &ary_Alloc(args);
 6161        *arg << "-hashfld:";
 6162        Smallstr100_Print(parent.cmd.hashfld, *arg);
 6163    }
 6164
 6165    if (parent.cmd.sortfld != "") {
 6166        cstring *arg = &ary_Alloc(args);
 6167        *arg << "-sortfld:";
 6168        Smallstr100_Print(parent.cmd.sortfld, *arg);
 6169    }
 6170
 6171    if (parent.cmd.unittest != "") {
 6172        cstring *arg = &ary_Alloc(args);
 6173        *arg << "-unittest:";
 6174        cstring_Print(parent.cmd.unittest, *arg);
 6175    }
 6176
 6177    if (parent.cmd.citest != "") {
 6178        cstring *arg = &ary_Alloc(args);
 6179        *arg << "-citest:";
 6180        cstring_Print(parent.cmd.citest, *arg);
 6181    }
 6182
 6183    if (parent.cmd.cppfunc != "") {
 6184        cstring *arg = &ary_Alloc(args);
 6185        *arg << "-cppfunc:";
 6186        cstring_Print(parent.cmd.cppfunc, *arg);
 6187    }
 6188
 6189    if (parent.cmd.xref != false) {
 6190        cstring *arg = &ary_Alloc(args);
 6191        *arg << "-xref:";
 6192        bool_Print(parent.cmd.xref, *arg);
 6193    }
 6194
 6195    if (parent.cmd.via != "") {
 6196        cstring *arg = &ary_Alloc(args);
 6197        *arg << "-via:";
 6198        cstring_Print(parent.cmd.via, *arg);
 6199    }
 6200
 6201    if (parent.cmd.write != false) {
 6202        cstring *arg = &ary_Alloc(args);
 6203        *arg << "-write:";
 6204        bool_Print(parent.cmd.write, *arg);
 6205    }
 6206
 6207    if (parent.cmd.e != false) {
 6208        cstring *arg = &ary_Alloc(args);
 6209        *arg << "-e:";
 6210        bool_Print(parent.cmd.e, *arg);
 6211    }
 6212
 6213    if (parent.cmd.comment != "") {
 6214        cstring *arg = &ary_Alloc(args);
 6215        *arg << "-comment:";
 6216        cstring_Print(parent.cmd.comment, *arg);
 6217    }
 6218
 6219    if (parent.cmd.sandbox != false) {
 6220        cstring *arg = &ary_Alloc(args);
 6221        *arg << "-sandbox:";
 6222        bool_Print(parent.cmd.sandbox, *arg);
 6223    }
 6224
 6225    if (parent.cmd.test != false) {
 6226        cstring *arg = &ary_Alloc(args);
 6227        *arg << "-test:";
 6228        bool_Print(parent.cmd.test, *arg);
 6229    }
 6230
 6231    if (parent.cmd.showcpp != false) {
 6232        cstring *arg = &ary_Alloc(args);
 6233        *arg << "-showcpp:";
 6234        bool_Print(parent.cmd.showcpp, *arg);
 6235    }
 6236
 6237    if (parent.cmd.msgtype != "") {
 6238        cstring *arg = &ary_Alloc(args);
 6239        *arg << "-msgtype:";
 6240        cstring_Print(parent.cmd.msgtype, *arg);
 6241    }
 6242
 6243    if (parent.cmd.anonfld != false) {
 6244        cstring *arg = &ary_Alloc(args);
 6245        *arg << "-anonfld:";
 6246        bool_Print(parent.cmd.anonfld, *arg);
 6247    }
 6248    for (int i=1; i < algo_lib::_db.cmdline.verbose; ++i) {
 6249        ary_Alloc(args) << "-verbose";
 6250    }
 6251}
 6252
 6253// --- command.acr_ed_proc..Uninit
 6254void command::acr_ed_proc_Uninit(command::acr_ed_proc& parent) {
 6255    command::acr_ed_proc &row = parent; (void)row;
 6256
 6257    // command.acr_ed_proc.acr_ed.Uninit (Exec)  //
 6258    acr_ed_Kill(parent); // kill child, ensure forward progress
 6259}
 6260
 6261// --- command.acr_in.ns.Print
 6262// Print back to string
 6263void command::ns_Print(command::acr_in& parent, algo::cstring &out) {
 6264    Regx_Print(parent.ns, out);
 6265}
 6266
 6267// --- command.acr_in.ns.ReadStrptrMaybe
 6268// Read Regx from string
 6269// Convert string to field. Return success value
 6270bool command::ns_ReadStrptrMaybe(command::acr_in& parent, algo::strptr in) {
 6271    bool retval = true;
 6272    Regx_ReadSql(parent.ns, in, true);
 6273    return retval;
 6274}
 6275
 6276// --- command.acr_in.notssimfile.Print
 6277// Print back to string
 6278void command::notssimfile_Print(command::acr_in& parent, algo::cstring &out) {
 6279    Regx_Print(parent.notssimfile, out);
 6280}
 6281
 6282// --- command.acr_in.notssimfile.ReadStrptrMaybe
 6283// Read Regx from string
 6284// Convert string to field. Return success value
 6285bool command::notssimfile_ReadStrptrMaybe(command::acr_in& parent, algo::strptr in) {
 6286    bool retval = true;
 6287    Regx_ReadSql(parent.notssimfile, in, true);
 6288    return retval;
 6289}
 6290
 6291// --- command.acr_in.r.Print
 6292// Print back to string
 6293void command::r_Print(command::acr_in& parent, algo::cstring &out) {
 6294    Regx_Print(parent.r, out);
 6295}
 6296
 6297// --- command.acr_in.r.ReadStrptrMaybe
 6298// Read Regx from string
 6299// Convert string to field. Return success value
 6300bool command::r_ReadStrptrMaybe(command::acr_in& parent, algo::strptr in) {
 6301    bool retval = true;
 6302    Regx_ReadSql(parent.r, in, true);
 6303    return retval;
 6304}
 6305
 6306// --- command.acr_in..ReadFieldMaybe
 6307bool command::acr_in_ReadFieldMaybe(command::acr_in& parent, algo::strptr field, algo::strptr strval) {
 6308    bool retval = true;
 6309    command::FieldId field_id;
 6310    (void)value_SetStrptrMaybe(field_id,field);
 6311    switch(field_id) {
 6312        case command_FieldId_ns: {
 6313            retval = ns_ReadStrptrMaybe(parent, strval);
 6314            break;
 6315        }
 6316        case command_FieldId_data: {
 6317            retval = bool_ReadStrptrMaybe(parent.data, strval);
 6318            break;
 6319        }
 6320        case command_FieldId_sigcheck: {
 6321            retval = bool_ReadStrptrMaybe(parent.sigcheck, strval);
 6322            break;
 6323        }
 6324        case command_FieldId_list: {
 6325            retval = bool_ReadStrptrMaybe(parent.list, strval);
 6326            break;
 6327        }
 6328        case command_FieldId_t: {
 6329            retval = bool_ReadStrptrMaybe(parent.t, strval);
 6330            break;
 6331        }
 6332        case command_FieldId_data_dir: {
 6333            retval = algo::cstring_ReadStrptrMaybe(parent.data_dir, strval);
 6334            break;
 6335        }
 6336        case command_FieldId_schema: {
 6337            retval = algo::cstring_ReadStrptrMaybe(parent.schema, strval);
 6338            break;
 6339        }
 6340        case command_FieldId_related: {
 6341            retval = algo::cstring_ReadStrptrMaybe(parent.related, strval);
 6342            break;
 6343        }
 6344        case command_FieldId_notssimfile: {
 6345            retval = notssimfile_ReadStrptrMaybe(parent, strval);
 6346            break;
 6347        }
 6348        case command_FieldId_checkable: {
 6349            retval = bool_ReadStrptrMaybe(parent.checkable, strval);
 6350            break;
 6351        }
 6352        case command_FieldId_r: {
 6353            retval = r_ReadStrptrMaybe(parent, strval);
 6354            break;
 6355        }
 6356        default: break;
 6357    }
 6358    if (!retval) {
 6359        algo_lib::AppendErrtext("attr",field);
 6360    }
 6361    return retval;
 6362}
 6363
 6364// --- command.acr_in..ReadTupleMaybe
 6365// Read fields of command::acr_in from attributes of ascii tuple TUPLE
 6366bool command::acr_in_ReadTupleMaybe(command::acr_in &parent, algo::Tuple &tuple) {
 6367    bool retval = true;
 6368    int anon_idx = 0;
 6369    ind_beg(algo::Tuple_attrs_curs,attr,tuple) {
 6370        if (ch_N(attr.name) == 0) {
 6371            attr.name = acr_in_GetAnon(parent, anon_idx++);
 6372        }
 6373        retval = acr_in_ReadFieldMaybe(parent, attr.name, attr.value);
 6374        if (!retval) {
 6375            break;
 6376        }
 6377    }ind_end;
 6378    return retval;
 6379}
 6380
 6381// --- command.acr_in..Init
 6382// Set all fields to initial values.
 6383void command::acr_in_Init(command::acr_in& parent) {
 6384    Regx_ReadSql(parent.ns, "", true);
 6385    parent.data = bool(false);
 6386    parent.sigcheck = bool(true);
 6387    parent.list = bool(false);
 6388    parent.t = bool(false);
 6389    parent.data_dir = algo::strptr("data");
 6390    parent.schema = algo::strptr("data");
 6391    parent.related = algo::strptr("");
 6392    Regx_ReadSql(parent.notssimfile, "", true);
 6393    parent.checkable = bool(false);
 6394    Regx_ReadSql(parent.r, "", true);
 6395}
 6396
 6397// --- command.acr_in..ToCmdline
 6398// Convenience function that returns a full command line
 6399// Assume command is in a directory called bin
 6400tempstr command::acr_in_ToCmdline(command::acr_in& row) {
 6401    tempstr ret;
 6402    ret << "bin/acr_in ";
 6403    acr_in_PrintArgv(row, ret);
 6404    // inherit less intense verbose, debug options
 6405    for (int i = 1; i < algo_lib::_db.cmdline.verbose; i++) {
 6406        ret << " -verbose";
 6407    }
 6408    for (int i = 1; i < algo_lib::_db.cmdline.debug; i++) {
 6409        ret << " -debug";
 6410    }
 6411    return ret;
 6412}
 6413
 6414// --- command.acr_in..PrintArgv
 6415// print string representation of ROW to string STR
 6416// cfmt:command.acr_in.Argv  printfmt:Auto
 6417void command::acr_in_PrintArgv(command::acr_in& row, algo::cstring& str) {
 6418    algo::tempstr temp;
 6419    (void)temp;
 6420    (void)str;
 6421    ch_RemoveAll(temp);
 6422    command::ns_Print(const_cast<command::acr_in&>(row), temp);
 6423    str << " -ns:";
 6424    strptr_PrintBash(temp,str);
 6425    if (!(row.data == false)) {
 6426        ch_RemoveAll(temp);
 6427        bool_Print(row.data, temp);
 6428        str << " -data:";
 6429        strptr_PrintBash(temp,str);
 6430    }
 6431    if (!(row.sigcheck == true)) {
 6432        ch_RemoveAll(temp);
 6433        bool_Print(row.sigcheck, temp);
 6434        str << " -sigcheck:";
 6435        strptr_PrintBash(temp,str);
 6436    }
 6437    if (!(row.list == false)) {
 6438        ch_RemoveAll(temp);
 6439        bool_Print(row.list, temp);
 6440        str << " -list:";
 6441        strptr_PrintBash(temp,str);
 6442    }
 6443    if (!(row.t == false)) {
 6444        ch_RemoveAll(temp);
 6445        bool_Print(row.t, temp);
 6446        str << " -t:";
 6447        strptr_PrintBash(temp,str);
 6448    }
 6449    if (!(row.data_dir == "data")) {
 6450        ch_RemoveAll(temp);
 6451        cstring_Print(row.data_dir, temp);
 6452        str << " -data_dir:";
 6453        strptr_PrintBash(temp,str);
 6454    }
 6455    if (!(row.schema == "data")) {
 6456        ch_RemoveAll(temp);
 6457        cstring_Print(row.schema, temp);
 6458        str << " -schema:";
 6459        strptr_PrintBash(temp,str);
 6460    }
 6461    if (!(row.related == "")) {
 6462        ch_RemoveAll(temp);
 6463        cstring_Print(row.related, temp);
 6464        str << " -related:";
 6465        strptr_PrintBash(temp,str);
 6466    }
 6467    if (!(row.notssimfile.expr == "")) {
 6468        ch_RemoveAll(temp);
 6469        command::notssimfile_Print(const_cast<command::acr_in&>(row), temp);
 6470        str << " -notssimfile:";
 6471        strptr_PrintBash(temp,str);
 6472    }
 6473    if (!(row.checkable == false)) {
 6474        ch_RemoveAll(temp);
 6475        bool_Print(row.checkable, temp);
 6476        str << " -checkable:";
 6477        strptr_PrintBash(temp,str);
 6478    }
 6479    if (!(row.r.expr == "")) {
 6480        ch_RemoveAll(temp);
 6481        command::r_Print(const_cast<command::acr_in&>(row), temp);
 6482        str << " -r:";
 6483        strptr_PrintBash(temp,str);
 6484    }
 6485}
 6486
 6487// --- command.acr_in..GetAnon
 6488algo::strptr command::acr_in_GetAnon(command::acr_in &parent, i32 idx) {
 6489    (void)parent;//only to avoid -Wunused-parameter
 6490    switch(idx) {
 6491        case(0): return strptr("ns", 2);
 6492        default: return algo::strptr();
 6493    }
 6494}
 6495
 6496// --- command.acr_in..NArgs
 6497// Used with command lines
 6498// Return # of command-line arguments that must follow this argument
 6499// If FIELD is invalid, return -1
 6500i32 command::acr_in_NArgs(command::FieldId field, algo::strptr& out_dflt, bool* out_anon) {
 6501    i32 retval = 1;
 6502    switch (field) {
 6503        case command_FieldId_ns: { // $comment
 6504            *out_anon = true;
 6505        } break;
 6506        case command_FieldId_data: { // $comment
 6507            *out_anon = false;
 6508            retval=0;
 6509            out_dflt="Y";
 6510        } break;
 6511        case command_FieldId_sigcheck: { // bool: no argument required but value may be specified as data:Y
 6512            *out_anon = false;
 6513            retval=0;
 6514            out_dflt="Y";
 6515        } break;
 6516        case command_FieldId_list: { // bool: no argument required but value may be specified as sigcheck:Y
 6517            *out_anon = false;
 6518            retval=0;
 6519            out_dflt="Y";
 6520        } break;
 6521        case command_FieldId_t: { // bool: no argument required but value may be specified as list:Y
 6522            *out_anon = false;
 6523            retval=0;
 6524            out_dflt="Y";
 6525        } break;
 6526        case command_FieldId_data_dir: { // bool: no argument required but value may be specified as t:Y
 6527            *out_anon = false;
 6528        } break;
 6529        case command_FieldId_schema: { // bool: no argument required but value may be specified as t:Y
 6530            *out_anon = false;
 6531        } break;
 6532        case command_FieldId_related: { // bool: no argument required but value may be specified as t:Y
 6533            *out_anon = false;
 6534        } break;
 6535        case command_FieldId_notssimfile: { // bool: no argument required but value may be specified as t:Y
 6536            *out_anon = false;
 6537        } break;
 6538        case command_FieldId_checkable: { // bool: no argument required but value may be specified as t:Y
 6539            *out_anon = false;
 6540            retval=0;
 6541            out_dflt="Y";
 6542        } break;
 6543        case command_FieldId_r: { // bool: no argument required but value may be specified as checkable:Y
 6544            *out_anon = false;
 6545        } break;
 6546        default:
 6547        retval=-1; // unrecognized
 6548    }
 6549    return retval;
 6550}
 6551
 6552// --- command.acr_in_proc.acr_in.Start
 6553// Start subprocess
 6554// If subprocess already running, do nothing. Otherwise, start it
 6555int command::acr_in_Start(command::acr_in_proc& parent) {
 6556    int retval = 0;
 6557    if (parent.pid == 0) {
 6558        verblog(acr_in_ToCmdline(parent)); // maybe print command
 6559#ifdef WIN32
 6560        algo_lib::ResolveExecFname(parent.path);
 6561        tempstr cmdline(acr_in_ToCmdline(parent));
 6562        parent.pid = dospawn(Zeroterm(parent.path),Zeroterm(cmdline),parent.timeout,parent.fstdin,parent.fstdout,parent.fstderr);
 6563#else
 6564        parent.pid = fork();
 6565        if (parent.pid == 0) { // child
 6566            algo_lib::DieWithParent();
 6567            if (parent.timeout > 0) {
 6568                alarm(parent.timeout);
 6569            }
 6570            if (retval==0) retval=algo_lib::ApplyRedirect(parent.fstdin , 0);
 6571            if (retval==0) retval=algo_lib::ApplyRedirect(parent.fstdout, 1);
 6572            if (retval==0) retval=algo_lib::ApplyRedirect(parent.fstderr, 2);
 6573            if (retval==0) retval= acr_in_Execv(parent);
 6574            if (retval != 0) { // if start fails, print error
 6575                int err=errno;
 6576                prerr("command.acr_in_execv"
 6577                <<Keyval("errno",err)
 6578                <<Keyval("errstr",strerror(err))
 6579                <<Keyval("comment","Execv failed"));
 6580            }
 6581            _exit(127); // if failed to start, exit anyway
 6582        } else if (parent.pid == -1) {
 6583            retval = errno; // failed to fork
 6584        }
 6585#endif
 6586    }
 6587    parent.status = parent.pid > 0 ? 0 : -1; // if didn't start, set error status
 6588    return retval;
 6589}
 6590
 6591// --- command.acr_in_proc.acr_in.StartRead
 6592// Start subprocess & Read output
 6593algo::Fildes command::acr_in_StartRead(command::acr_in_proc& parent, algo_lib::FFildes &read) {
 6594    int pipefd[2];
 6595    int rc=pipe(pipefd);
 6596    (void)rc;
 6597    read.fd.value = pipefd[0];
 6598    parent.fstdout  << ">&" << pipefd[1];
 6599    acr_in_Start(parent);
 6600    (void)close(pipefd[1]);
 6601    return read.fd;
 6602}
 6603
 6604// --- command.acr_in_proc.acr_in.Kill
 6605// Kill subprocess and wait
 6606void command::acr_in_Kill(command::acr_in_proc& parent) {
 6607    if (parent.pid != 0) {
 6608        kill(parent.pid,9);
 6609        acr_in_Wait(parent);
 6610    }
 6611}
 6612
 6613// --- command.acr_in_proc.acr_in.Wait
 6614// Wait for subprocess to return
 6615void command::acr_in_Wait(command::acr_in_proc& parent) {
 6616    if (parent.pid > 0) {
 6617        int wait_flags = 0;
 6618        int wait_status = 0;
 6619        int rc = -1;
 6620        do {
 6621            // really wait for subprocess to exit
 6622            rc = waitpid(parent.pid,&wait_status,wait_flags);
 6623        } while (rc==-1 && errno==EINTR);
 6624        if (rc == parent.pid) {
 6625            parent.status = wait_status;
 6626            parent.pid = 0;
 6627        }
 6628    }
 6629}
 6630
 6631// --- command.acr_in_proc.acr_in.Exec
 6632// Start + Wait
 6633// Execute subprocess and return exit code
 6634int command::acr_in_Exec(command::acr_in_proc& parent) {
 6635    acr_in_Start(parent);
 6636    acr_in_Wait(parent);
 6637    return parent.status;
 6638}
 6639
 6640// --- command.acr_in_proc.acr_in.ExecX
 6641// Start + Wait, throw exception on error
 6642// Execute subprocess; throw human-readable exception on error
 6643void command::acr_in_ExecX(command::acr_in_proc& parent) {
 6644    int rc = acr_in_Exec(parent);
 6645    vrfy(rc==0, tempstr() << "algo_lib.exec" << Keyval("cmd",acr_in_ToCmdline(parent))
 6646    << Keyval("comment",algo::DescribeWaitStatus(parent.status)));
 6647}
 6648
 6649// --- command.acr_in_proc.acr_in.Execv
 6650// Call execv()
 6651// Call execv with specified parameters
 6652int command::acr_in_Execv(command::acr_in_proc& parent) {
 6653    int ret = 0;
 6654    algo::StringAry args;
 6655    acr_in_ToArgv(parent, args);
 6656    char **argv = (char**)alloca((ary_N(args)+1)*sizeof(*argv));
 6657    ind_beg(algo::StringAry_ary_curs,arg,args) {
 6658        argv[ind_curs(arg).index] = Zeroterm(arg);
 6659    }ind_end;
 6660    argv[ary_N(args)] = NULL;
 6661    // if parent.path is relative, search for it in PATH
 6662    algo_lib::ResolveExecFname(parent.path);
 6663    ret = execv(Zeroterm(parent.path),argv);
 6664    return ret;
 6665}
 6666
 6667// --- command.acr_in_proc.acr_in.ToCmdline
 6668algo::tempstr command::acr_in_ToCmdline(command::acr_in_proc& parent) {
 6669    algo::tempstr retval;
 6670    retval << parent.path << " ";
 6671    command::acr_in_PrintArgv(parent.cmd,retval);
 6672    if (ch_N(parent.fstdin)) {
 6673        retval << " " << parent.fstdin;
 6674    }
 6675    if (ch_N(parent.fstdout)) {
 6676        retval << " " << parent.fstdout;
 6677    }
 6678    if (ch_N(parent.fstderr)) {
 6679        retval << " 2" << parent.fstderr;
 6680    }
 6681    return retval;
 6682}
 6683
 6684// --- command.acr_in_proc.acr_in.ToArgv
 6685// Form array from the command line
 6686void command::acr_in_ToArgv(command::acr_in_proc& parent, algo::StringAry& args) {
 6687    ary_RemoveAll(args);
 6688    ary_Alloc(args) << parent.path;
 6689
 6690    if (parent.cmd.ns.expr != "") {
 6691        cstring *arg = &ary_Alloc(args);
 6692        *arg << "-ns:";
 6693        command::ns_Print(parent.cmd, *arg);
 6694    }
 6695
 6696    if (parent.cmd.data != false) {
 6697        cstring *arg = &ary_Alloc(args);
 6698        *arg << "-data:";
 6699        bool_Print(parent.cmd.data, *arg);
 6700    }
 6701
 6702    if (parent.cmd.sigcheck != true) {
 6703        cstring *arg = &ary_Alloc(args);
 6704        *arg << "-sigcheck:";
 6705        bool_Print(parent.cmd.sigcheck, *arg);
 6706    }
 6707
 6708    if (parent.cmd.list != false) {
 6709        cstring *arg = &ary_Alloc(args);
 6710        *arg << "-list:";
 6711        bool_Print(parent.cmd.list, *arg);
 6712    }
 6713
 6714    if (parent.cmd.t != false) {
 6715        cstring *arg = &ary_Alloc(args);
 6716        *arg << "-t:";
 6717        bool_Print(parent.cmd.t, *arg);
 6718    }
 6719
 6720    if (parent.cmd.data_dir != "data") {
 6721        cstring *arg = &ary_Alloc(args);
 6722        *arg << "-data_dir:";
 6723        cstring_Print(parent.cmd.data_dir, *arg);
 6724    }
 6725
 6726    if (parent.cmd.schema != "data") {
 6727        cstring *arg = &ary_Alloc(args);
 6728        *arg << "-schema:";
 6729        cstring_Print(parent.cmd.schema, *arg);
 6730    }
 6731
 6732    if (parent.cmd.related != "") {
 6733        cstring *arg = &ary_Alloc(args);
 6734        *arg << "-related:";
 6735        cstring_Print(parent.cmd.related, *arg);
 6736    }
 6737
 6738    if (parent.cmd.notssimfile.expr != "") {
 6739        cstring *arg = &ary_Alloc(args);
 6740        *arg << "-notssimfile:";
 6741        command::notssimfile_Print(parent.cmd, *arg);
 6742    }
 6743
 6744    if (parent.cmd.checkable != false) {
 6745        cstring *arg = &ary_Alloc(args);
 6746        *arg << "-checkable:";
 6747        bool_Print(parent.cmd.checkable, *arg);
 6748    }
 6749
 6750    if (parent.cmd.r.expr != "") {
 6751        cstring *arg = &ary_Alloc(args);
 6752        *arg << "-r:";
 6753        command::r_Print(parent.cmd, *arg);
 6754    }
 6755    for (int i=1; i < algo_lib::_db.cmdline.verbose; ++i) {
 6756        ary_Alloc(args) << "-verbose";
 6757    }
 6758}
 6759
 6760// --- command.acr_in_proc..Uninit
 6761void command::acr_in_proc_Uninit(command::acr_in_proc& parent) {
 6762    command::acr_in_proc &row = parent; (void)row;
 6763
 6764    // command.acr_in_proc.acr_in.Uninit (Exec)  //
 6765    acr_in_Kill(parent); // kill child, ensure forward progress
 6766}
 6767
 6768// --- command.acr_my.nsdb.Print
 6769// Print back to string
 6770void command::nsdb_Print(command::acr_my& parent, algo::cstring &out) {
 6771    Regx_Print(parent.nsdb, out);
 6772}
 6773
 6774// --- command.acr_my.nsdb.ReadStrptrMaybe
 6775// Read Regx from string
 6776// Convert string to field. Return success value
 6777bool command::nsdb_ReadStrptrMaybe(command::acr_my& parent, algo::strptr in) {
 6778    bool retval = true;
 6779    Regx_ReadSql(parent.nsdb, in, true);
 6780    return retval;
 6781}
 6782
 6783// --- command.acr_my..ReadFieldMaybe
 6784bool command::acr_my_ReadFieldMaybe(command::acr_my& parent, algo::strptr field, algo::strptr strval) {
 6785    bool retval = true;
 6786    command::FieldId field_id;
 6787    (void)value_SetStrptrMaybe(field_id,field);
 6788    switch(field_id) {
 6789        case command_FieldId_nsdb: {
 6790            retval = nsdb_ReadStrptrMaybe(parent, strval);
 6791            break;
 6792        }
 6793        case command_FieldId_in: {
 6794            retval = algo::cstring_ReadStrptrMaybe(parent.in, strval);
 6795            break;
 6796        }
 6797        case command_FieldId_schema: {
 6798            retval = algo::cstring_ReadStrptrMaybe(parent.schema, strval);
 6799            break;
 6800        }
 6801        case command_FieldId_fldfunc: {
 6802            retval = bool_ReadStrptrMaybe(parent.fldfunc, strval);
 6803            break;
 6804        }
 6805        case command_FieldId_fkey: {
 6806            retval = bool_ReadStrptrMaybe(parent.fkey, strval);
 6807            break;
 6808        }
 6809        case command_FieldId_e: {
 6810            retval = bool_ReadStrptrMaybe(parent.e, strval);
 6811            break;
 6812        }
 6813        case command_FieldId_start: {
 6814            retval = bool_ReadStrptrMaybe(parent.start, strval);
 6815            break;
 6816        }
 6817        case command_FieldId_stop: {
 6818            retval = bool_ReadStrptrMaybe(parent.stop, strval);
 6819            break;
 6820        }
 6821        case command_FieldId_abort: {
 6822            retval = bool_ReadStrptrMaybe(parent.abort, strval);
 6823            break;
 6824        }
 6825        case command_FieldId_shell: {
 6826            retval = bool_ReadStrptrMaybe(parent.shell, strval);
 6827            break;
 6828        }
 6829        case command_FieldId_serv: {
 6830            retval = bool_ReadStrptrMaybe(parent.serv, strval);
 6831            break;
 6832        }
 6833        default: break;
 6834    }
 6835    if (!retval) {
 6836        algo_lib::AppendErrtext("attr",field);
 6837    }
 6838    return retval;
 6839}
 6840
 6841// --- command.acr_my..ReadTupleMaybe
 6842// Read fields of command::acr_my from attributes of ascii tuple TUPLE
 6843bool command::acr_my_ReadTupleMaybe(command::acr_my &parent, algo::Tuple &tuple) {
 6844    bool retval = true;
 6845    int anon_idx = 0;
 6846    ind_beg(algo::Tuple_attrs_curs,attr,tuple) {
 6847        if (ch_N(attr.name) == 0) {
 6848            attr.name = acr_my_GetAnon(parent, anon_idx++);
 6849        }
 6850        retval = acr_my_ReadFieldMaybe(parent, attr.name, attr.value);
 6851        if (!retval) {
 6852            break;
 6853        }
 6854    }ind_end;
 6855    return retval;
 6856}
 6857
 6858// --- command.acr_my..Init
 6859// Set all fields to initial values.
 6860void command::acr_my_Init(command::acr_my& parent) {
 6861    Regx_ReadSql(parent.nsdb, "", true);
 6862    parent.in = algo::strptr("data");
 6863    parent.schema = algo::strptr("data");
 6864    parent.fldfunc = bool(false);
 6865    parent.fkey = bool(false);
 6866    parent.e = bool(false);
 6867    parent.start = bool(false);
 6868    parent.stop = bool(false);
 6869    parent.abort = bool(false);
 6870    parent.shell = bool(false);
 6871    parent.serv = bool(false);
 6872}
 6873
 6874// --- command.acr_my..ToCmdline
 6875// Convenience function that returns a full command line
 6876// Assume command is in a directory called bin
 6877tempstr command::acr_my_ToCmdline(command::acr_my& row) {
 6878    tempstr ret;
 6879    ret << "bin/acr_my ";
 6880    acr_my_PrintArgv(row, ret);
 6881    // inherit less intense verbose, debug options
 6882    for (int i = 1; i < algo_lib::_db.cmdline.verbose; i++) {
 6883        ret << " -verbose";
 6884    }
 6885    for (int i = 1; i < algo_lib::_db.cmdline.debug; i++) {
 6886        ret << " -debug";
 6887    }
 6888    return ret;
 6889}
 6890
 6891// --- command.acr_my..PrintArgv
 6892// print string representation of ROW to string STR
 6893// cfmt:command.acr_my.Argv  printfmt:Tuple
 6894void command::acr_my_PrintArgv(command::acr_my& row, algo::cstring& str) {
 6895    algo::tempstr temp;
 6896    (void)temp;
 6897    (void)str;
 6898    ch_RemoveAll(temp);
 6899    command::nsdb_Print(const_cast<command::acr_my&>(row), temp);
 6900    str << " -nsdb:";
 6901    strptr_PrintBash(temp,str);
 6902    if (!(row.in == "data")) {
 6903        ch_RemoveAll(temp);
 6904        cstring_Print(row.in, temp);
 6905        str << " -in:";
 6906        strptr_PrintBash(temp,str);
 6907    }
 6908    if (!(row.schema == "data")) {
 6909        ch_RemoveAll(temp);
 6910        cstring_Print(row.schema, temp);
 6911        str << " -schema:";
 6912        strptr_PrintBash(temp,str);
 6913    }
 6914    if (!(row.fldfunc == false)) {
 6915        ch_RemoveAll(temp);
 6916        bool_Print(row.fldfunc, temp);
 6917        str << " -fldfunc:";
 6918        strptr_PrintBash(temp,str);
 6919    }
 6920    if (!(row.fkey == false)) {
 6921        ch_RemoveAll(temp);
 6922        bool_Print(row.fkey, temp);
 6923        str << " -fkey:";
 6924        strptr_PrintBash(temp,str);
 6925    }
 6926    if (!(row.e == false)) {
 6927        ch_RemoveAll(temp);
 6928        bool_Print(row.e, temp);
 6929        str << " -e:";
 6930        strptr_PrintBash(temp,str);
 6931    }
 6932    if (!(row.start == false)) {
 6933        ch_RemoveAll(temp);
 6934        bool_Print(row.start, temp);
 6935        str << " -start:";
 6936        strptr_PrintBash(temp,str);
 6937    }
 6938    if (!(row.stop == false)) {
 6939        ch_RemoveAll(temp);
 6940        bool_Print(row.stop, temp);
 6941        str << " -stop:";
 6942        strptr_PrintBash(temp,str);
 6943    }
 6944    if (!(row.abort == false)) {
 6945        ch_RemoveAll(temp);
 6946        bool_Print(row.abort, temp);
 6947        str << " -abort:";
 6948        strptr_PrintBash(temp,str);
 6949    }
 6950    if (!(row.shell == false)) {
 6951        ch_RemoveAll(temp);
 6952        bool_Print(row.shell, temp);
 6953        str << " -shell:";
 6954        strptr_PrintBash(temp,str);
 6955    }
 6956    if (!(row.serv == false)) {
 6957        ch_RemoveAll(temp);
 6958        bool_Print(row.serv, temp);
 6959        str << " -serv:";
 6960        strptr_PrintBash(temp,str);
 6961    }
 6962}
 6963
 6964// --- command.acr_my..GetAnon
 6965algo::strptr command::acr_my_GetAnon(command::acr_my &parent, i32 idx) {
 6966    (void)parent;//only to avoid -Wunused-parameter
 6967    switch(idx) {
 6968        case(0): return strptr("nsdb", 4);
 6969        default: return algo::strptr();
 6970    }
 6971}
 6972
 6973// --- command.acr_my..NArgs
 6974// Used with command lines
 6975// Return # of command-line arguments that must follow this argument
 6976// If FIELD is invalid, return -1
 6977i32 command::acr_my_NArgs(command::FieldId field, algo::strptr& out_dflt, bool* out_anon) {
 6978    i32 retval = 1;
 6979    switch (field) {
 6980        case command_FieldId_nsdb: { // $comment
 6981            *out_anon = true;
 6982        } break;
 6983        case command_FieldId_in: { // $comment
 6984            *out_anon = false;
 6985        } break;
 6986        case command_FieldId_schema: { // $comment
 6987            *out_anon = false;
 6988        } break;
 6989        case command_FieldId_fldfunc: { // $comment
 6990            *out_anon = false;
 6991            retval=0;
 6992            out_dflt="Y";
 6993        } break;
 6994        case command_FieldId_fkey: { // bool: no argument required but value may be specified as fldfunc:Y
 6995            *out_anon = false;
 6996            retval=0;
 6997            out_dflt="Y";
 6998        } break;
 6999        case command_FieldId_e: { // bool: no argument required but value may be specified as fkey:Y
 7000            *out_anon = false;
 7001            retval=0;
 7002            out_dflt="Y";
 7003        } break;
 7004        case command_FieldId_start: { // bool: no argument required but value may be specified as e:Y
 7005            *out_anon = false;
 7006            retval=0;
 7007            out_dflt="Y";
 7008        } break;
 7009        case command_FieldId_stop: { // bool: no argument required but value may be specified as start:Y
 7010            *out_anon = false;
 7011            retval=0;
 7012            out_dflt="Y";
 7013        } break;
 7014        case command_FieldId_abort: { // bool: no argument required but value may be specified as stop:Y
 7015            *out_anon = false;
 7016            retval=0;
 7017            out_dflt="Y";
 7018        } break;
 7019        case command_FieldId_shell: { // bool: no argument required but value may be specified as abort:Y
 7020            *out_anon = false;
 7021            retval=0;
 7022            out_dflt="Y";
 7023        } break;
 7024        case command_FieldId_serv: { // bool: no argument required but value may be specified as shell:Y
 7025            *out_anon = false;
 7026            retval=0;
 7027            out_dflt="Y";
 7028        } break;
 7029        default:
 7030        retval=-1; // unrecognized
 7031    }
 7032    return retval;
 7033}
 7034
 7035// --- command.acr_my_proc.acr_my.Start
 7036// Start subprocess
 7037// If subprocess already running, do nothing. Otherwise, start it
 7038int command::acr_my_Start(command::acr_my_proc& parent) {
 7039    int retval = 0;
 7040    if (parent.pid == 0) {
 7041        verblog(acr_my_ToCmdline(parent)); // maybe print command
 7042#ifdef WIN32
 7043        algo_lib::ResolveExecFname(parent.path);
 7044        tempstr cmdline(acr_my_ToCmdline(parent));
 7045        parent.pid = dospawn(Zeroterm(parent.path),Zeroterm(cmdline),parent.timeout,parent.fstdin,parent.fstdout,parent.fstderr);
 7046#else
 7047        parent.pid = fork();
 7048        if (parent.pid == 0) { // child
 7049            algo_lib::DieWithParent();
 7050            if (parent.timeout > 0) {
 7051                alarm(parent.timeout);
 7052            }
 7053            if (retval==0) retval=algo_lib::ApplyRedirect(parent.fstdin , 0);
 7054            if (retval==0) retval=algo_lib::ApplyRedirect(parent.fstdout, 1);
 7055            if (retval==0) retval=algo_lib::ApplyRedirect(parent.fstderr, 2);
 7056            if (retval==0) retval= acr_my_Execv(parent);
 7057            if (retval != 0) { // if start fails, print error
 7058                int err=errno;
 7059                prerr("command.acr_my_execv"
 7060                <<Keyval("errno",err)
 7061                <<Keyval("errstr",strerror(err))
 7062                <<Keyval("comment","Execv failed"));
 7063            }
 7064            _exit(127); // if failed to start, exit anyway
 7065        } else if (parent.pid == -1) {
 7066            retval = errno; // failed to fork
 7067        }
 7068#endif
 7069    }
 7070    parent.status = parent.pid > 0 ? 0 : -1; // if didn't start, set error status
 7071    return retval;
 7072}
 7073
 7074// --- command.acr_my_proc.acr_my.StartRead
 7075// Start subprocess & Read output
 7076algo::Fildes command::acr_my_StartRead(command::acr_my_proc& parent, algo_lib::FFildes &read) {
 7077    int pipefd[2];
 7078    int rc=pipe(pipefd);
 7079    (void)rc;
 7080    read.fd.value = pipefd[0];
 7081    parent.fstdout  << ">&" << pipefd[1];
 7082    acr_my_Start(parent);
 7083    (void)close(pipefd[1]);
 7084    return read.fd;
 7085}
 7086
 7087// --- command.acr_my_proc.acr_my.Kill
 7088// Kill subprocess and wait
 7089void command::acr_my_Kill(command::acr_my_proc& parent) {
 7090    if (parent.pid != 0) {
 7091        kill(parent.pid,9);
 7092        acr_my_Wait(parent);
 7093    }
 7094}
 7095
 7096// --- command.acr_my_proc.acr_my.Wait
 7097// Wait for subprocess to return
 7098void command::acr_my_Wait(command::acr_my_proc& parent) {
 7099    if (parent.pid > 0) {
 7100        int wait_flags = 0;
 7101        int wait_status = 0;
 7102        int rc = -1;
 7103        do {
 7104            // really wait for subprocess to exit
 7105            rc = waitpid(parent.pid,&wait_status,wait_flags);
 7106        } while (rc==-1 && errno==EINTR);
 7107        if (rc == parent.pid) {
 7108            parent.status = wait_status;
 7109            parent.pid = 0;
 7110        }
 7111    }
 7112}
 7113
 7114// --- command.acr_my_proc.acr_my.Exec
 7115// Start + Wait
 7116// Execute subprocess and return exit code
 7117int command::acr_my_Exec(command::acr_my_proc& parent) {
 7118    acr_my_Start(parent);
 7119    acr_my_Wait(parent);
 7120    return parent.status;
 7121}
 7122
 7123// --- command.acr_my_proc.acr_my.ExecX
 7124// Start + Wait, throw exception on error
 7125// Execute subprocess; throw human-readable exception on error
 7126void command::acr_my_ExecX(command::acr_my_proc& parent) {
 7127    int rc = acr_my_Exec(parent);
 7128    vrfy(rc==0, tempstr() << "algo_lib.exec" << Keyval("cmd",acr_my_ToCmdline(parent))
 7129    << Keyval("comment",algo::DescribeWaitStatus(parent.status)));
 7130}
 7131
 7132// --- command.acr_my_proc.acr_my.Execv
 7133// Call execv()
 7134// Call execv with specified parameters
 7135int command::acr_my_Execv(command::acr_my_proc& parent) {
 7136    int ret = 0;
 7137    algo::StringAry args;
 7138    acr_my_ToArgv(parent, args);
 7139    char **argv = (char**)alloca((ary_N(args)+1)*sizeof(*argv));
 7140    ind_beg(algo::StringAry_ary_curs,arg,args) {
 7141        argv[ind_curs(arg).index] = Zeroterm(arg);
 7142    }ind_end;
 7143    argv[ary_N(args)] = NULL;
 7144    // if parent.path is relative, search for it in PATH
 7145    algo_lib::ResolveExecFname(parent.path);
 7146    ret = execv(Zeroterm(parent.path),argv);
 7147    return ret;
 7148}
 7149
 7150// --- command.acr_my_proc.acr_my.ToCmdline
 7151algo::tempstr command::acr_my_ToCmdline(command::acr_my_proc& parent) {
 7152    algo::tempstr retval;
 7153    retval << parent.path << " ";
 7154    command::acr_my_PrintArgv(parent.cmd,retval);
 7155    if (ch_N(parent.fstdin)) {
 7156        retval << " " << parent.fstdin;
 7157    }
 7158    if (ch_N(parent.fstdout)) {
 7159        retval << " " << parent.fstdout;
 7160    }
 7161    if (ch_N(parent.fstderr)) {
 7162        retval << " 2" << parent.fstderr;
 7163    }
 7164    return retval;
 7165}
 7166
 7167// --- command.acr_my_proc.acr_my.ToArgv
 7168// Form array from the command line
 7169void command::acr_my_ToArgv(command::acr_my_proc& parent, algo::StringAry& args) {
 7170    ary_RemoveAll(args);
 7171    ary_Alloc(args) << parent.path;
 7172
 7173    if (parent.cmd.nsdb.expr != "") {
 7174        cstring *arg = &ary_Alloc(args);
 7175        *arg << "-nsdb:";
 7176        command::nsdb_Print(parent.cmd, *arg);
 7177    }
 7178
 7179    if (parent.cmd.in != "data") {
 7180        cstring *arg = &ary_Alloc(args);
 7181        *arg << "-in:";
 7182        cstring_Print(parent.cmd.in, *arg);
 7183    }
 7184
 7185    if (parent.cmd.schema != "data") {
 7186        cstring *arg = &ary_Alloc(args);
 7187        *arg << "-schema:";
 7188        cstring_Print(parent.cmd.schema, *arg);
 7189    }
 7190
 7191    if (parent.cmd.fldfunc != false) {
 7192        cstring *arg = &ary_Alloc(args);
 7193        *arg << "-fldfunc:";
 7194        bool_Print(parent.cmd.fldfunc, *arg);
 7195    }
 7196
 7197    if (parent.cmd.fkey != false) {
 7198        cstring *arg = &ary_Alloc(args);
 7199        *arg << "-fkey:";
 7200        bool_Print(parent.cmd.fkey, *arg);
 7201    }
 7202
 7203    if (parent.cmd.e != false) {
 7204        cstring *arg = &ary_Alloc(args);
 7205        *arg << "-e:";
 7206        bool_Print(parent.cmd.e, *arg);
 7207    }
 7208
 7209    if (parent.cmd.start != false) {
 7210        cstring *arg = &ary_Alloc(args);
 7211        *arg << "-start:";
 7212        bool_Print(parent.cmd.start, *arg);
 7213    }
 7214
 7215    if (parent.cmd.stop != false) {
 7216        cstring *arg = &ary_Alloc(args);
 7217        *arg << "-stop:";
 7218        bool_Print(parent.cmd.stop, *arg);
 7219    }
 7220
 7221    if (parent.cmd.abort != false) {
 7222        cstring *arg = &ary_Alloc(args);
 7223        *arg << "-abort:";
 7224        bool_Print(parent.cmd.abort, *arg);
 7225    }
 7226
 7227    if (parent.cmd.shell != false) {
 7228        cstring *arg = &ary_Alloc(args);
 7229        *arg << "-shell:";
 7230        bool_Print(parent.cmd.shell, *arg);
 7231    }
 7232
 7233    if (parent.cmd.serv != false) {
 7234        cstring *arg = &ary_Alloc(args);
 7235        *arg << "-serv:";
 7236        bool_Print(parent.cmd.serv, *arg);
 7237    }
 7238    for (int i=1; i < algo_lib::_db.cmdline.verbose; ++i) {
 7239        ary_Alloc(args) << "-verbose";
 7240    }
 7241}
 7242
 7243// --- command.acr_my_proc..Uninit
 7244void command::acr_my_proc_Uninit(command::acr_my_proc& parent) {
 7245    command::acr_my_proc &row = parent; (void)row;
 7246
 7247    // command.acr_my_proc.acr_my.Uninit (Exec)  //
 7248    acr_my_Kill(parent); // kill child, ensure forward progress
 7249}
 7250
 7251// --- command.acr_proc.acr.Start
 7252// Start subprocess
 7253// If subprocess already running, do nothing. Otherwise, start it
 7254int command::acr_Start(command::acr_proc& parent) {
 7255    int retval = 0;
 7256    if (parent.pid == 0) {
 7257        verblog(acr_ToCmdline(parent)); // maybe print command
 7258#ifdef WIN32
 7259        algo_lib::ResolveExecFname(parent.path);
 7260        tempstr cmdline(acr_ToCmdline(parent));
 7261        parent.pid = dospawn(Zeroterm(parent.path),Zeroterm(cmdline),parent.timeout,parent.fstdin,parent.fstdout,parent.fstderr);
 7262#else
 7263        parent.pid = fork();
 7264        if (parent.pid == 0) { // child
 7265            algo_lib::DieWithParent();
 7266            if (parent.timeout > 0) {
 7267                alarm(parent.timeout);
 7268            }
 7269            if (retval==0) retval=algo_lib::ApplyRedirect(parent.fstdin , 0);
 7270            if (retval==0) retval=algo_lib::ApplyRedirect(parent.fstdout, 1);
 7271            if (retval==0) retval=algo_lib::ApplyRedirect(parent.fstderr, 2);
 7272            if (retval==0) retval= acr_Execv(parent);
 7273            if (retval != 0) { // if start fails, print error
 7274                int err=errno;
 7275                prerr("command.acr_execv"
 7276                <<Keyval("errno",err)
 7277                <<Keyval("errstr",strerror(err))
 7278                <<Keyval("comment","Execv failed"));
 7279            }
 7280            _exit(127); // if failed to start, exit anyway
 7281        } else if (parent.pid == -1) {
 7282            retval = errno; // failed to fork
 7283        }
 7284#endif
 7285    }
 7286    parent.status = parent.pid > 0 ? 0 : -1; // if didn't start, set error status
 7287    return retval;
 7288}
 7289
 7290// --- command.acr_proc.acr.StartRead
 7291// Start subprocess & Read output
 7292algo::Fildes command::acr_StartRead(command::acr_proc& parent, algo_lib::FFildes &read) {
 7293    int pipefd[2];
 7294    int rc=pipe(pipefd);
 7295    (void)rc;
 7296    read.fd.value = pipefd[0];
 7297    parent.fstdout  << ">&" << pipefd[1];
 7298    acr_Start(parent);
 7299    (void)close(pipefd[1]);
 7300    return read.fd;
 7301}
 7302
 7303// --- command.acr_proc.acr.Kill
 7304// Kill subprocess and wait
 7305void command::acr_Kill(command::acr_proc& parent) {
 7306    if (parent.pid != 0) {
 7307        kill(parent.pid,9);
 7308        acr_Wait(parent);
 7309    }
 7310}
 7311
 7312// --- command.acr_proc.acr.Wait
 7313// Wait for subprocess to return
 7314void command::acr_Wait(command::acr_proc& parent) {
 7315    if (parent.pid > 0) {
 7316        int wait_flags = 0;
 7317        int wait_status = 0;
 7318        int rc = -1;
 7319        do {
 7320            // really wait for subprocess to exit
 7321            rc = waitpid(parent.pid,&wait_status,wait_flags);
 7322        } while (rc==-1 && errno==EINTR);
 7323        if (rc == parent.pid) {
 7324            parent.status = wait_status;
 7325            parent.pid = 0;
 7326        }
 7327    }
 7328}
 7329
 7330// --- command.acr_proc.acr.Exec
 7331// Start + Wait
 7332// Execute subprocess and return exit code
 7333int command::acr_Exec(command::acr_proc& parent) {
 7334    acr_Start(parent);
 7335    acr_Wait(parent);
 7336    return parent.status;
 7337}
 7338
 7339// --- command.acr_proc.acr.ExecX
 7340// Start + Wait, throw exception on error
 7341// Execute subprocess; throw human-readable exception on error
 7342void command::acr_ExecX(command::acr_proc& parent) {
 7343    int rc = acr_Exec(parent);
 7344    vrfy(rc==0, tempstr() << "algo_lib.exec" << Keyval("cmd",acr_ToCmdline(parent))
 7345    << Keyval("comment",algo::DescribeWaitStatus(parent.status)));
 7346}
 7347
 7348// --- command.acr_proc.acr.Execv
 7349// Call execv()
 7350// Call execv with specified parameters
 7351int command::acr_Execv(command::acr_proc& parent) {
 7352    int ret = 0;
 7353    algo::StringAry args;
 7354    acr_ToArgv(parent, args);
 7355    char **argv = (char**)alloca((ary_N(args)+1)*sizeof(*argv));
 7356    ind_beg(algo::StringAry_ary_curs,arg,args) {
 7357        argv[ind_curs(arg).index] = Zeroterm(arg);
 7358    }ind_end;
 7359    argv[ary_N(args)] = NULL;
 7360    // if parent.path is relative, search for it in PATH
 7361    algo_lib::ResolveExecFname(parent.path);
 7362    ret = execv(Zeroterm(parent.path),argv);
 7363    return ret;
 7364}
 7365
 7366// --- command.acr_proc.acr.ToCmdline
 7367algo::tempstr command::acr_ToCmdline(command::acr_proc& parent) {
 7368    algo::tempstr retval;
 7369    retval << parent.path << " ";
 7370    command::acr_PrintArgv(parent.cmd,retval);
 7371    if (ch_N(parent.fstdin)) {
 7372        retval << " " << parent.fstdin;
 7373    }
 7374    if (ch_N(parent.fstdout)) {
 7375        retval << " " << parent.fstdout;
 7376    }
 7377    if (ch_N(parent.fstderr)) {
 7378        retval << " 2" << parent.fstderr;
 7379    }
 7380    return retval;
 7381}
 7382
 7383// --- command.acr_proc.acr.ToArgv
 7384// Form array from the command line
 7385void command::acr_ToArgv(command::acr_proc& parent, algo::StringAry& args) {
 7386    ary_RemoveAll(args);
 7387    ary_Alloc(args) << parent.path;
 7388
 7389    if (parent.cmd.query != "") {
 7390        cstring *arg = &ary_Alloc(args);
 7391        *arg << "-query:";
 7392        cstring_Print(parent.cmd.query, *arg);
 7393    }
 7394    ind_beg(command::acr_where_curs,value,parent.cmd) {
 7395        cstring *arg = &ary_Alloc(args);
 7396        *arg << "-where:";
 7397        cstring_Print(value, *arg);
 7398    }ind_end;
 7399
 7400    if (parent.cmd.in != "data") {
 7401        cstring *arg = &ary_Alloc(args);
 7402        *arg << "-in:";
 7403        cstring_Print(parent.cmd.in, *arg);
 7404    }
 7405
 7406    if (parent.cmd.del != false) {
 7407        cstring *arg = &ary_Alloc(args);
 7408        *arg << "-del:";
 7409        bool_Print(parent.cmd.del, *arg);
 7410    }
 7411
 7412    if (parent.cmd.sel != false) {
 7413        cstring *arg = &ary_Alloc(args);
 7414        *arg << "-sel:";
 7415        bool_Print(parent.cmd.sel, *arg);
 7416    }
 7417
 7418    if (parent.cmd.insert != false) {
 7419        cstring *arg = &ary_Alloc(args);
 7420        *arg << "-insert:";
 7421        bool_Print(parent.cmd.insert, *arg);
 7422    }
 7423
 7424    if (parent.cmd.replace != false) {
 7425        cstring *arg = &ary_Alloc(args);
 7426        *arg << "-replace:";
 7427        bool_Print(parent.cmd.replace, *arg);
 7428    }
 7429
 7430    if (parent.cmd.update != false) {
 7431        cstring *arg = &ary_Alloc(args);
 7432        *arg << "-update:";
 7433        bool_Print(parent.cmd.update, *arg);
 7434    }
 7435
 7436    if (parent.cmd.merge != false) {
 7437        cstring *arg = &ary_Alloc(args);
 7438        *arg << "-merge:";
 7439        bool_Print(parent.cmd.merge, *arg);
 7440    }
 7441
 7442    if (parent.cmd.unused != false) {
 7443        cstring *arg = &ary_Alloc(args);
 7444        *arg << "-unused:";
 7445        bool_Print(parent.cmd.unused, *arg);
 7446    }
 7447
 7448    if (parent.cmd.trunc != false) {
 7449        cstring *arg = &ary_Alloc(args);
 7450        *arg << "-trunc:";
 7451        bool_Print(parent.cmd.trunc, *arg);
 7452    }
 7453
 7454    if (parent.cmd.check != false) {
 7455        cstring *arg = &ary_Alloc(args);
 7456        *arg << "-check:";
 7457        bool_Print(parent.cmd.check, *arg);
 7458    }
 7459
 7460    if (parent.cmd.selerr != true) {
 7461        cstring *arg = &ary_Alloc(args);
 7462        *arg << "-selerr:";
 7463        bool_Print(parent.cmd.selerr, *arg);
 7464    }
 7465
 7466    if (parent.cmd.maxshow != 100) {
 7467        cstring *arg = &ary_Alloc(args);
 7468        *arg << "-maxshow:";
 7469        i32_Print(parent.cmd.maxshow, *arg);
 7470    }
 7471
 7472    if (parent.cmd.write != false) {
 7473        cstring *arg = &ary_Alloc(args);
 7474        *arg << "-write:";
 7475        bool_Print(parent.cmd.write, *arg);
 7476    }
 7477
 7478    if (parent.cmd.rename != "") {
 7479        cstring *arg = &ary_Alloc(args);
 7480        *arg << "-rename:";
 7481        cstring_Print(parent.cmd.rename, *arg);
 7482    }
 7483
 7484    if (parent.cmd.nup != 0) {
 7485        cstring *arg = &ary_Alloc(args);
 7486        *arg << "-nup:";
 7487        i32_Print(parent.cmd.nup, *arg);
 7488    }
 7489
 7490    if (parent.cmd.ndown != 0) {
 7491        cstring *arg = &ary_Alloc(args);
 7492        *arg << "-ndown:";
 7493        i32_Print(parent.cmd.ndown, *arg);
 7494    }
 7495
 7496    if (parent.cmd.l != false) {
 7497        cstring *arg = &ary_Alloc(args);
 7498        *arg << "-l:";
 7499        bool_Print(parent.cmd.l, *arg);
 7500    }
 7501
 7502    if (parent.cmd.xref != false) {
 7503        cstring *arg = &ary_Alloc(args);
 7504        *arg << "-xref:";
 7505        bool_Print(parent.cmd.xref, *arg);
 7506    }
 7507
 7508    if (parent.cmd.fldfunc != false) {
 7509        cstring *arg = &ary_Alloc(args);
 7510        *arg << "-fldfunc:";
 7511        bool_Print(parent.cmd.fldfunc, *arg);
 7512    }
 7513
 7514    if (parent.cmd.maxgroup != 25) {
 7515        cstring *arg = &ary_Alloc(args);
 7516        *arg << "-maxgroup:";
 7517        i32_Print(parent.cmd.maxgroup, *arg);
 7518    }
 7519
 7520    if (parent.cmd.pretty != true) {
 7521        cstring *arg = &ary_Alloc(args);
 7522        *arg << "-pretty:";
 7523        bool_Print(parent.cmd.pretty, *arg);
 7524    }
 7525
 7526    if (parent.cmd.tree != false) {
 7527        cstring *arg = &ary_Alloc(args);
 7528        *arg << "-tree:";
 7529        bool_Print(parent.cmd.tree, *arg);
 7530    }
 7531
 7532    if (parent.cmd.loose != false) {
 7533        cstring *arg = &ary_Alloc(args);
 7534        *arg << "-loose:";
 7535        bool_Print(parent.cmd.loose, *arg);
 7536    }
 7537
 7538    if (parent.cmd.my != false) {
 7539        cstring *arg = &ary_Alloc(args);
 7540        *arg << "-my:";
 7541        bool_Print(parent.cmd.my, *arg);
 7542    }
 7543
 7544    if (parent.cmd.schema != "data") {
 7545        cstring *arg = &ary_Alloc(args);
 7546        *arg << "-schema:";
 7547        cstring_Print(parent.cmd.schema, *arg);
 7548    }
 7549
 7550    if (parent.cmd.e != false) {
 7551        cstring *arg = &ary_Alloc(args);
 7552        *arg << "-e:";
 7553        bool_Print(parent.cmd.e, *arg);
 7554    }
 7555
 7556    if (parent.cmd.t != false) {
 7557        cstring *arg = &ary_Alloc(args);
 7558        *arg << "-t:";
 7559        bool_Print(parent.cmd.t, *arg);
 7560    }
 7561
 7562    if (parent.cmd.g != false) {
 7563        cstring *arg = &ary_Alloc(args);
 7564        *arg << "-g:";
 7565        bool_Print(parent.cmd.g, *arg);
 7566    }
 7567
 7568    if (parent.cmd.x != false) {
 7569        cstring *arg = &ary_Alloc(args);
 7570        *arg << "-x:";
 7571        bool_Print(parent.cmd.x, *arg);
 7572    }
 7573
 7574    if (parent.cmd.rowid != false) {
 7575        cstring *arg = &ary_Alloc(args);
 7576        *arg << "-rowid:";
 7577        bool_Print(parent.cmd.rowid, *arg);
 7578    }
 7579
 7580    if (parent.cmd.cmt != false) {
 7581        cstring *arg = &ary_Alloc(args);
 7582        *arg << "-cmt:";
 7583        bool_Print(parent.cmd.cmt, *arg);
 7584    }
 7585
 7586    if (parent.cmd.report != true) {
 7587        cstring *arg = &ary_Alloc(args);
 7588        *arg << "-report:";
 7589        bool_Print(parent.cmd.report, *arg);
 7590    }
 7591
 7592    if (parent.cmd.print != true) {
 7593        cstring *arg = &ary_Alloc(args);
 7594        *arg << "-print:";
 7595        bool_Print(parent.cmd.print, *arg);
 7596    }
 7597
 7598    if (parent.cmd.cmd != "") {
 7599        cstring *arg = &ary_Alloc(args);
 7600        *arg << "-cmd:";
 7601        cstring_Print(parent.cmd.cmd, *arg);
 7602    }
 7603    ind_beg(command::acr_field_curs,value,parent.cmd) {
 7604        cstring *arg = &ary_Alloc(args);
 7605        *arg << "-field:";
 7606        cstring_Print(value, *arg);
 7607    }ind_end;
 7608
 7609    if (parent.cmd.regxof != "") {
 7610        cstring *arg = &ary_Alloc(args);
 7611        *arg << "-regxof:";
 7612        cstring_Print(parent.cmd.regxof, *arg);
 7613    }
 7614
 7615    if (parent.cmd.meta != false) {
 7616        cstring *arg = &ary_Alloc(args);
 7617        *arg << "-meta:";
 7618        bool_Print(parent.cmd.meta, *arg);
 7619    }
 7620    for (int i=1; i < algo_lib::_db.cmdline.verbose; ++i) {
 7621        ary_Alloc(args) << "-verbose";
 7622    }
 7623}
 7624
 7625// --- command.acr_proc..Uninit
 7626void command::acr_proc_Uninit(command::acr_proc& parent) {
 7627    command::acr_proc &row = parent; (void)row;
 7628
 7629    // command.acr_proc.acr.Uninit (Exec)  //
 7630    acr_Kill(parent); // kill child, ensure forward progress
 7631}
 7632
 7633// --- command.amc.trace.Print
 7634// Print back to string
 7635void command::trace_Print(command::amc& parent, algo::cstring &out) {
 7636    Regx_Print(parent.trace, out);
 7637}
 7638
 7639// --- command.amc.trace.ReadStrptrMaybe
 7640// Read Regx from string
 7641// Convert string to field. Return success value
 7642bool command::trace_ReadStrptrMaybe(command::amc& parent, algo::strptr in) {
 7643    bool retval = true;
 7644    Regx_ReadSql(parent.trace, in, true);
 7645    return retval;
 7646}
 7647
 7648// --- command.amc..ReadFieldMaybe
 7649bool command::amc_ReadFieldMaybe(command::amc& parent, algo::strptr field, algo::strptr strval) {
 7650    bool retval = true;
 7651    command::FieldId field_id;
 7652    (void)value_SetStrptrMaybe(field_id,field);
 7653    switch(field_id) {
 7654        case command_FieldId_in_dir: {
 7655            retval = algo::cstring_ReadStrptrMaybe(parent.in_dir, strval);
 7656            break;
 7657        }
 7658        case command_FieldId_query: {
 7659            retval = algo::cstring_ReadStrptrMaybe(parent.query, strval);
 7660            break;
 7661        }
 7662        case command_FieldId_out_dir: {
 7663            retval = algo::cstring_ReadStrptrMaybe(parent.out_dir, strval);
 7664            break;
 7665        }
 7666        case command_FieldId_proto: {
 7667            retval = bool_ReadStrptrMaybe(parent.proto, strval);
 7668            break;
 7669        }
 7670        case command_FieldId_report: {
 7671            retval = bool_ReadStrptrMaybe(parent.report, strval);
 7672            break;
 7673        }
 7674        case command_FieldId_e: {
 7675            retval = bool_ReadStrptrMaybe(parent.e, strval);
 7676            break;
 7677        }
 7678        case command_FieldId_trace: {
 7679            retval = trace_ReadStrptrMaybe(parent, strval);
 7680            break;
 7681        }
 7682        default: break;
 7683    }
 7684    if (!retval) {
 7685        algo_lib::AppendErrtext("attr",field);
 7686    }
 7687    return retval;
 7688}
 7689
 7690// --- command.amc..ReadTupleMaybe
 7691// Read fields of command::amc from attributes of ascii tuple TUPLE
 7692bool command::amc_ReadTupleMaybe(command::amc &parent, algo::Tuple &tuple) {
 7693    bool retval = true;
 7694    int anon_idx = 0;
 7695    ind_beg(algo::Tuple_attrs_curs,attr,tuple) {
 7696        if (ch_N(attr.name) == 0) {
 7697            attr.name = amc_GetAnon(parent, anon_idx++);
 7698        }
 7699        retval = amc_ReadFieldMaybe(parent, attr.name, attr.value);
 7700        if (!retval) {
 7701            break;
 7702        }
 7703    }ind_end;
 7704    return retval;
 7705}
 7706
 7707// --- command.amc..Init
 7708// Set all fields to initial values.
 7709void command::amc_Init(command::amc& parent) {
 7710    parent.in_dir = algo::strptr("data");
 7711    parent.query = algo::strptr("");
 7712    parent.out_dir = algo::strptr(".");
 7713    parent.proto = bool(false);
 7714    parent.report = bool(true);
 7715    parent.e = bool(false);
 7716    Regx_ReadSql(parent.trace, "", true);
 7717}
 7718
 7719// --- command.amc..ToCmdline
 7720// Convenience function that returns a full command line
 7721// Assume command is in a directory called bin
 7722tempstr command::amc_ToCmdline(command::amc& row) {
 7723    tempstr ret;
 7724    ret << "bin/amc ";
 7725    amc_PrintArgv(row, ret);
 7726    // inherit less intense verbose, debug options
 7727    for (int i = 1; i < algo_lib::_db.cmdline.verbose; i++) {
 7728        ret << " -verbose";
 7729    }
 7730    for (int i = 1; i < algo_lib::_db.cmdline.debug; i++) {
 7731        ret << " -debug";
 7732    }
 7733    return ret;
 7734}
 7735
 7736// --- command.amc..PrintArgv
 7737// print string representation of ROW to string STR
 7738// cfmt:command.amc.Argv  printfmt:Auto
 7739void command::amc_PrintArgv(command::amc& row, algo::cstring& str) {
 7740    algo::tempstr temp;
 7741    (void)temp;
 7742    (void)str;
 7743    if (!(row.in_dir == "data")) {
 7744        ch_RemoveAll(temp);
 7745        cstring_Print(row.in_dir, temp);
 7746        str << " -in_dir:";
 7747        strptr_PrintBash(temp,str);
 7748    }
 7749    ch_RemoveAll(temp);
 7750    cstring_Print(row.query, temp);
 7751    str << " -query:";
 7752    strptr_PrintBash(temp,str);
 7753    if (!(row.out_dir == ".")) {
 7754        ch_RemoveAll(temp);
 7755        cstring_Print(row.out_dir, temp);
 7756        str << " -out_dir:";
 7757        strptr_PrintBash(temp,str);
 7758    }
 7759    if (!(row.proto == false)) {
 7760        ch_RemoveAll(temp);
 7761        bool_Print(row.proto, temp);
 7762        str << " -proto:";
 7763        strptr_PrintBash(temp,str);
 7764    }
 7765    if (!(row.report == true)) {
 7766        ch_RemoveAll(temp);
 7767        bool_Print(row.report, temp);
 7768        str << " -report:";
 7769        strptr_PrintBash(temp,str);
 7770    }
 7771    if (!(row.e == false)) {
 7772        ch_RemoveAll(temp);
 7773        bool_Print(row.e, temp);
 7774        str << " -e:";
 7775        strptr_PrintBash(temp,str);
 7776    }
 7777    if (!(row.trace.expr == "")) {
 7778        ch_RemoveAll(temp);
 7779        command::trace_Print(const_cast<command::amc&>(row), temp);
 7780        str << " -trace:";
 7781        strptr_PrintBash(temp,str);
 7782    }
 7783}
 7784
 7785// --- command.amc..GetAnon
 7786algo::strptr command::amc_GetAnon(command::amc &parent, i32 idx) {
 7787    (void)parent;//only to avoid -Wunused-parameter
 7788    switch(idx) {
 7789        case(0): return strptr("query", 5);
 7790        default: return algo::strptr();
 7791    }
 7792}
 7793
 7794// --- command.amc..NArgs
 7795// Used with command lines
 7796// Return # of command-line arguments that must follow this argument
 7797// If FIELD is invalid, return -1
 7798i32 command::amc_NArgs(command::FieldId field, algo::strptr& out_dflt, bool* out_anon) {
 7799    i32 retval = 1;
 7800    switch (field) {
 7801        case command_FieldId_in_dir: { // $comment
 7802            *out_anon = false;
 7803        } break;
 7804        case command_FieldId_query: { // $comment
 7805            *out_anon = true;
 7806        } break;
 7807        case command_FieldId_out_dir: { // $comment
 7808            *out_anon = false;
 7809        } break;
 7810        case command_FieldId_proto: { // $comment
 7811            *out_anon = false;
 7812            retval=0;
 7813            out_dflt="Y";
 7814        } break;
 7815        case command_FieldId_report: { // bool: no argument required but value may be specified as proto:Y
 7816            *out_anon = false;
 7817            retval=0;
 7818            out_dflt="Y";
 7819        } break;
 7820        case command_FieldId_e: { // bool: no argument required but value may be specified as report:Y
 7821            *out_anon = false;
 7822            retval=0;
 7823            out_dflt="Y";
 7824        } break;
 7825        case command_FieldId_trace: { // bool: no argument required but value may be specified as e:Y
 7826            *out_anon = false;
 7827        } break;
 7828        default:
 7829        retval=-1; // unrecognized
 7830    }
 7831    return retval;
 7832}
 7833
 7834// --- command.amc_gc.target.Print
 7835// Print back to string
 7836void command::target_Print(command::amc_gc& parent, algo::cstring &out) {
 7837    Regx_Print(parent.target, out);
 7838}
 7839
 7840// --- command.amc_gc.target.ReadStrptrMaybe
 7841// Read Regx from string
 7842// Convert string to field. Return success value
 7843bool command::target_ReadStrptrMaybe(command::amc_gc& parent, algo::strptr in) {
 7844    bool retval = true;
 7845    Regx_ReadSql(parent.target, in, true);
 7846    return retval;
 7847}
 7848
 7849// --- command.amc_gc.key.Print
 7850// Print back to string
 7851void command::key_Print(command::amc_gc& parent, algo::cstring &out) {
 7852    Regx_Print(parent.key, out);
 7853}
 7854
 7855// --- command.amc_gc.key.ReadStrptrMaybe
 7856// Read Regx from string
 7857// Convert string to field. Return success value
 7858bool command::key_ReadStrptrMaybe(command::amc_gc& parent, algo::strptr in) {
 7859    bool retval = true;
 7860    Regx_ReadSql(parent.key, in, true);
 7861    return retval;
 7862}
 7863
 7864// --- command.amc_gc..ReadFieldMaybe
 7865bool command::amc_gc_ReadFieldMaybe(command::amc_gc& parent, algo::strptr field, algo::strptr strval) {
 7866    bool retval = true;
 7867    command::FieldId field_id;
 7868    (void)value_SetStrptrMaybe(field_id,field);
 7869    switch(field_id) {
 7870        case command_FieldId_target: {
 7871            retval = target_ReadStrptrMaybe(parent, strval);
 7872            break;
 7873        }
 7874        case command_FieldId_key: {
 7875            retval = key_ReadStrptrMaybe(parent, strval);
 7876            break;
 7877        }
 7878        case command_FieldId_include: {
 7879            retval = bool_ReadStrptrMaybe(parent.include, strval);
 7880            break;
 7881        }
 7882        case command_FieldId_in: {
 7883            retval = algo::cstring_ReadStrptrMaybe(parent.in, strval);
 7884            break;
 7885        }
 7886        default: break;
 7887    }
 7888    if (!retval) {
 7889        algo_lib::AppendErrtext("attr",field);
 7890    }
 7891    return retval;
 7892}
 7893
 7894// --- command.amc_gc..ReadTupleMaybe
 7895// Read fields of command::amc_gc from attributes of ascii tuple TUPLE
 7896bool command::amc_gc_ReadTupleMaybe(command::amc_gc &parent, algo::Tuple &tuple) {
 7897    bool retval = true;
 7898    ind_beg(algo::Tuple_attrs_curs,attr,tuple) {
 7899        retval = amc_gc_ReadFieldMaybe(parent, attr.name, attr.value);
 7900        if (!retval) {
 7901            break;
 7902        }
 7903    }ind_end;
 7904    return retval;
 7905}
 7906
 7907// --- command.amc_gc..Init
 7908// Set all fields to initial values.
 7909void command::amc_gc_Init(command::amc_gc& parent) {
 7910    Regx_ReadSql(parent.target, "%", true);
 7911    Regx_ReadSql(parent.key, "", true);
 7912    parent.include = bool(false);
 7913    parent.in = algo::strptr("data");
 7914}
 7915
 7916// --- command.amc_gc..ToCmdline
 7917// Convenience function that returns a full command line
 7918// Assume command is in a directory called bin
 7919tempstr command::amc_gc_ToCmdline(command::amc_gc& row) {
 7920    tempstr ret;
 7921    ret << "bin/amc_gc ";
 7922    amc_gc_PrintArgv(row, ret);
 7923    // inherit less intense verbose, debug options
 7924    for (int i = 1; i < algo_lib::_db.cmdline.verbose; i++) {
 7925        ret << " -verbose";
 7926    }
 7927    for (int i = 1; i < algo_lib::_db.cmdline.debug; i++) {
 7928        ret << " -debug";
 7929    }
 7930    return ret;
 7931}
 7932
 7933// --- command.amc_gc..PrintArgv
 7934// print string representation of ROW to string STR
 7935// cfmt:command.amc_gc.Argv  printfmt:Tuple
 7936void command::amc_gc_PrintArgv(command::amc_gc& row, algo::cstring& str) {
 7937    algo::tempstr temp;
 7938    (void)temp;
 7939    (void)str;
 7940    if (!(row.target.expr == "%")) {
 7941        ch_RemoveAll(temp);
 7942        command::target_Print(const_cast<command::amc_gc&>(row), temp);
 7943        str << " -target:";
 7944        strptr_PrintBash(temp,str);
 7945    }
 7946    if (!(row.key.expr == "")) {
 7947        ch_RemoveAll(temp);
 7948        command::key_Print(const_cast<command::amc_gc&>(row), temp);
 7949        str << " -key:";
 7950        strptr_PrintBash(temp,str);
 7951    }
 7952    if (!(row.include == false)) {
 7953        ch_RemoveAll(temp);
 7954        bool_Print(row.include, temp);
 7955        str << " -include:";
 7956        strptr_PrintBash(temp,str);
 7957    }
 7958    if (!(row.in == "data")) {
 7959        ch_RemoveAll(temp);
 7960        cstring_Print(row.in, temp);
 7961        str << " -in:";
 7962        strptr_PrintBash(temp,str);
 7963    }
 7964}
 7965
 7966// --- command.amc_gc..NArgs
 7967// Used with command lines
 7968// Return # of command-line arguments that must follow this argument
 7969// If FIELD is invalid, return -1
 7970i32 command::amc_gc_NArgs(command::FieldId field, algo::strptr& out_dflt, bool* out_anon) {
 7971    i32 retval = 1;
 7972    switch (field) {
 7973        case command_FieldId_target: { // $comment
 7974            *out_anon = false;
 7975        } break;
 7976        case command_FieldId_key: { // $comment
 7977            *out_anon = false;
 7978        } break;
 7979        case command_FieldId_include: { // $comment
 7980            *out_anon = false;
 7981            retval=0;
 7982            out_dflt="Y";
 7983        } break;
 7984        case command_FieldId_in: { // bool: no argument required but value may be specified as include:Y
 7985            *out_anon = false;
 7986        } break;
 7987        default:
 7988        retval=-1; // unrecognized
 7989    }
 7990    return retval;
 7991}
 7992
 7993// --- command.amc_gc_proc.amc_gc.Start
 7994// Start subprocess
 7995// If subprocess already running, do nothing. Otherwise, start it
 7996int command::amc_gc_Start(command::amc_gc_proc& parent) {
 7997    int retval = 0;
 7998    if (parent.pid == 0) {
 7999        verblog(amc_gc_ToCmdline(parent)); // maybe print command
 8000#ifdef WIN32
 8001        algo_lib::ResolveExecFname(parent.path);
 8002        tempstr cmdline(amc_gc_ToCmdline(parent));
 8003        parent.pid = dospawn(Zeroterm(parent.path),Zeroterm(cmdline),parent.timeout,parent.fstdin,parent.fstdout,parent.fstderr);
 8004#else
 8005        parent.pid = fork();
 8006        if (parent.pid == 0) { // child
 8007            algo_lib::DieWithParent();
 8008            if (parent.timeout > 0) {
 8009                alarm(parent.timeout);
 8010            }
 8011            if (retval==0) retval=algo_lib::ApplyRedirect(parent.fstdin , 0);
 8012            if (retval==0) retval=algo_lib::ApplyRedirect(parent.fstdout, 1);
 8013            if (retval==0) retval=algo_lib::ApplyRedirect(parent.fstderr, 2);
 8014            if (retval==0) retval= amc_gc_Execv(parent);
 8015            if (retval != 0) { // if start fails, print error
 8016                int err=errno;
 8017                prerr("command.amc_gc_execv"
 8018                <<Keyval("errno",err)
 8019                <<Keyval("errstr",strerror(err))
 8020                <<Keyval("comment","Execv failed"));
 8021            }
 8022            _exit(127); // if failed to start, exit anyway
 8023        } else if (parent.pid == -1) {
 8024            retval = errno; // failed to fork
 8025        }
 8026#endif
 8027    }
 8028    parent.status = parent.pid > 0 ? 0 : -1; // if didn't start, set error status
 8029    return retval;
 8030}
 8031
 8032// --- command.amc_gc_proc.amc_gc.StartRead
 8033// Start subprocess & Read output
 8034algo::Fildes command::amc_gc_StartRead(command::amc_gc_proc& parent, algo_lib::FFildes &read) {
 8035    int pipefd[2];
 8036    int rc=pipe(pipefd);
 8037    (void)rc;
 8038    read.fd.value = pipefd[0];
 8039    parent.fstdout  << ">&" << pipefd[1];
 8040    amc_gc_Start(parent);
 8041    (void)close(pipefd[1]);
 8042    return read.fd;
 8043}
 8044
 8045// --- command.amc_gc_proc.amc_gc.Kill
 8046// Kill subprocess and wait
 8047void command::amc_gc_Kill(command::amc_gc_proc& parent) {
 8048    if (parent.pid != 0) {
 8049        kill(parent.pid,9);
 8050        amc_gc_Wait(parent);
 8051    }
 8052}
 8053
 8054// --- command.amc_gc_proc.amc_gc.Wait
 8055// Wait for subprocess to return
 8056void command::amc_gc_Wait(command::amc_gc_proc& parent) {
 8057    if (parent.pid > 0) {
 8058        int wait_flags = 0;
 8059        int wait_status = 0;
 8060        int rc = -1;
 8061        do {
 8062            // really wait for subprocess to exit
 8063            rc = waitpid(parent.pid,&wait_status,wait_flags);
 8064        } while (rc==-1 && errno==EINTR);
 8065        if (rc == parent.pid) {
 8066            parent.status = wait_status;
 8067            parent.pid = 0;
 8068        }
 8069    }
 8070}
 8071
 8072// --- command.amc_gc_proc.amc_gc.Exec
 8073// Start + Wait
 8074// Execute subprocess and return exit code
 8075int command::amc_gc_Exec(command::amc_gc_proc& parent) {
 8076    amc_gc_Start(parent);
 8077    amc_gc_Wait(parent);
 8078    return parent.status;
 8079}
 8080
 8081// --- command.amc_gc_proc.amc_gc.ExecX
 8082// Start + Wait, throw exception on error
 8083// Execute subprocess; throw human-readable exception on error
 8084void command::amc_gc_ExecX(command::amc_gc_proc& parent) {
 8085    int rc = amc_gc_Exec(parent);
 8086    vrfy(rc==0, tempstr() << "algo_lib.exec" << Keyval("cmd",amc_gc_ToCmdline(parent))
 8087    << Keyval("comment",algo::DescribeWaitStatus(parent.status)));
 8088}
 8089
 8090// --- command.amc_gc_proc.amc_gc.Execv
 8091// Call execv()
 8092// Call execv with specified parameters
 8093int command::amc_gc_Execv(command::amc_gc_proc& parent) {
 8094    int ret = 0;
 8095    algo::StringAry args;
 8096    amc_gc_ToArgv(parent, args);
 8097    char **argv = (char**)alloca((ary_N(args)+1)*sizeof(*argv));
 8098    ind_beg(algo::StringAry_ary_curs,arg,args) {
 8099        argv[ind_curs(arg).index] = Zeroterm(arg);
 8100    }ind_end;
 8101    argv[ary_N(args)] = NULL;
 8102    // if parent.path is relative, search for it in PATH
 8103    algo_lib::ResolveExecFname(parent.path);
 8104    ret = execv(Zeroterm(parent.path),argv);
 8105    return ret;
 8106}
 8107
 8108// --- command.amc_gc_proc.amc_gc.ToCmdline
 8109algo::tempstr command::amc_gc_ToCmdline(command::amc_gc_proc& parent) {
 8110    algo::tempstr retval;
 8111    retval << parent.path << " ";
 8112    command::amc_gc_PrintArgv(parent.cmd,retval);
 8113    if (ch_N(parent.fstdin)) {
 8114        retval << " " << parent.fstdin;
 8115    }
 8116    if (ch_N(parent.fstdout)) {
 8117        retval << " " << parent.fstdout;
 8118    }
 8119    if (ch_N(parent.fstderr)) {
 8120        retval << " 2" << parent.fstderr;
 8121    }
 8122    return retval;
 8123}
 8124
 8125// --- command.amc_gc_proc.amc_gc.ToArgv
 8126// Form array from the command line
 8127void command::amc_gc_ToArgv(command::amc_gc_proc& parent, algo::StringAry& args) {
 8128    ary_RemoveAll(args);
 8129    ary_Alloc(args) << parent.path;
 8130
 8131    if (parent.cmd.target.expr != "%") {
 8132        cstring *arg = &ary_Alloc(args);
 8133        *arg << "-target:";
 8134        command::target_Print(parent.cmd, *arg);
 8135    }
 8136
 8137    if (parent.cmd.key.expr != "") {
 8138        cstring *arg = &ary_Alloc(args);
 8139        *arg << "-key:";
 8140        command::key_Print(parent.cmd, *arg);
 8141    }
 8142
 8143    if (parent.cmd.include != false) {
 8144        cstring *arg = &ary_Alloc(args);
 8145        *arg << "-include:";
 8146        bool_Print(parent.cmd.include, *arg);
 8147    }
 8148
 8149    if (parent.cmd.in != "data") {
 8150        cstring *arg = &ary_Alloc(args);
 8151        *arg << "-in:";
 8152        cstring_Print(parent.cmd.in, *arg);
 8153    }
 8154    for (int i=1; i < algo_lib::_db.cmdline.verbose; ++i) {
 8155        ary_Alloc(args) << "-verbose";
 8156    }
 8157}
 8158
 8159// --- command.amc_gc_proc..Uninit
 8160void command::amc_gc_proc_Uninit(command::amc_gc_proc& parent) {
 8161    command::amc_gc_proc &row = parent; (void)row;
 8162
 8163    // command.amc_gc_proc.amc_gc.Uninit (Exec)  //
 8164    amc_gc_Kill(parent); // kill child, ensure forward progress
 8165}
 8166
 8167// --- command.amc_js..ReadFieldMaybe
 8168bool command::amc_js_ReadFieldMaybe(command::amc_js& parent, algo::strptr field, algo::strptr strval) {
 8169    bool retval = true;
 8170    command::FieldId field_id;
 8171    (void)value_SetStrptrMaybe(field_id,field);
 8172    switch(field_id) {
 8173        case command_FieldId_in: {
 8174            retval = algo::cstring_ReadStrptrMaybe(parent.in, strval);
 8175            break;
 8176        }
 8177        default: break;
 8178    }
 8179    if (!retval) {
 8180        algo_lib::AppendErrtext("attr",field);
 8181    }
 8182    return retval;
 8183}
 8184
 8185// --- command.amc_js..ReadTupleMaybe
 8186// Read fields of command::amc_js from attributes of ascii tuple TUPLE
 8187bool command::amc_js_ReadTupleMaybe(command::amc_js &parent, algo::Tuple &tuple) {
 8188    bool retval = true;
 8189    ind_beg(algo::Tuple_attrs_curs,attr,tuple) {
 8190        retval = amc_js_ReadFieldMaybe(parent, attr.name, attr.value);
 8191        if (!retval) {
 8192            break;
 8193        }
 8194    }ind_end;
 8195    return retval;
 8196}
 8197
 8198// --- command.amc_js..ToCmdline
 8199// Convenience function that returns a full command line
 8200// Assume command is in a directory called bin
 8201tempstr command::amc_js_ToCmdline(command::amc_js& row) {
 8202    tempstr ret;
 8203    ret << "bin/amc_js ";
 8204    amc_js_PrintArgv(row, ret);
 8205    // inherit less intense verbose, debug options
 8206    for (int i = 1; i < algo_lib::_db.cmdline.verbose; i++) {
 8207        ret << " -verbose";
 8208    }
 8209    for (int i = 1; i < algo_lib::_db.cmdline.debug; i++) {
 8210        ret << " -debug";
 8211    }
 8212    return ret;
 8213}
 8214
 8215// --- command.amc_js..PrintArgv
 8216// print string representation of ROW to string STR
 8217// cfmt:command.amc_js.Argv  printfmt:Tuple
 8218void command::amc_js_PrintArgv(command::amc_js& row, algo::cstring& str) {
 8219    algo::tempstr temp;
 8220    (void)temp;
 8221    (void)str;
 8222    if (!(row.in == "data")) {
 8223        ch_RemoveAll(temp);
 8224        cstring_Print(row.in, temp);
 8225        str << " -in:";
 8226        strptr_PrintBash(temp,str);
 8227    }
 8228}
 8229
 8230// --- command.amc_js..NArgs
 8231// Used with command lines
 8232// Return # of command-line arguments that must follow this argument
 8233// If FIELD is invalid, return -1
 8234i32 command::amc_js_NArgs(command::FieldId field, algo::strptr& out_dflt, bool* out_anon) {
 8235    i32 retval = 1;
 8236    switch (field) {
 8237        case command_FieldId_in: { // $comment
 8238            *out_anon = false;
 8239        } break;
 8240        default:
 8241        retval=-1; // unrecognized
 8242    }
 8243    (void)out_dflt;//only to avoid -Wunused-parameter
 8244    return retval;
 8245}
 8246
 8247// --- command.amc_js_proc.amc_js.Start
 8248// Start subprocess
 8249// If subprocess already running, do nothing. Otherwise, start it
 8250int command::amc_js_Start(command::amc_js_proc& parent) {
 8251    int retval = 0;
 8252    if (parent.pid == 0) {
 8253        verblog(amc_js_ToCmdline(parent)); // maybe print command
 8254#ifdef WIN32
 8255        algo_lib::ResolveExecFname(parent.path);
 8256        tempstr cmdline(amc_js_ToCmdline(parent));
 8257        parent.pid = dospawn(Zeroterm(parent.path),Zeroterm(cmdline),parent.timeout,parent.fstdin,parent.fstdout,parent.fstderr);
 8258#else
 8259        parent.pid = fork();
 8260        if (parent.pid == 0) { // child
 8261            algo_lib::DieWithParent();
 8262            if (parent.timeout > 0) {
 8263                alarm(parent.timeout);
 8264            }
 8265            if (retval==0) retval=algo_lib::ApplyRedirect(parent.fstdin , 0);
 8266            if (retval==0) retval=algo_lib::ApplyRedirect(parent.fstdout, 1);
 8267            if (retval==0) retval=algo_lib::ApplyRedirect(parent.fstderr, 2);
 8268            if (retval==0) retval= amc_js_Execv(parent);
 8269            if (retval != 0) { // if start fails, print error
 8270                int err=errno;
 8271                prerr("command.amc_js_execv"
 8272                <<Keyval("errno",err)
 8273                <<Keyval("errstr",strerror(err))
 8274                <<Keyval("comment","Execv failed"));
 8275            }
 8276            _exit(127); // if failed to start, exit anyway
 8277        } else if (parent.pid == -1) {
 8278            retval = errno; // failed to fork
 8279        }
 8280#endif
 8281    }
 8282    parent.status = parent.pid > 0 ? 0 : -1; // if didn't start, set error status
 8283    return retval;
 8284}
 8285
 8286// --- command.amc_js_proc.amc_js.StartRead
 8287// Start subprocess & Read output
 8288algo::Fildes command::amc_js_StartRead(command::amc_js_proc& parent, algo_lib::FFildes &read) {
 8289    int pipefd[2];
 8290    int rc=pipe(pipefd);
 8291    (void)rc;
 8292    read.fd.value = pipefd[0];
 8293    parent.fstdout  << ">&" << pipefd[1];
 8294    amc_js_Start(parent);
 8295    (void)close(pipefd[1]);
 8296    return read.fd;
 8297}
 8298
 8299// --- command.amc_js_proc.amc_js.Kill
 8300// Kill subprocess and wait
 8301void command::amc_js_Kill(command::amc_js_proc& parent) {
 8302    if (parent.pid != 0) {
 8303        kill(parent.pid,9);
 8304        amc_js_Wait(parent);
 8305    }
 8306}
 8307
 8308// --- command.amc_js_proc.amc_js.Wait
 8309// Wait for subprocess to return
 8310void command::amc_js_Wait(command::amc_js_proc& parent) {
 8311    if (parent.pid > 0) {
 8312        int wait_flags = 0;
 8313        int wait_status = 0;
 8314        int rc = -1;
 8315        do {
 8316            // really wait for subprocess to exit
 8317            rc = waitpid(parent.pid,&wait_status,wait_flags);
 8318        } while (rc==-1 && errno==EINTR);
 8319        if (rc == parent.pid) {
 8320            parent.status = wait_status;
 8321            parent.pid = 0;
 8322        }
 8323    }
 8324}
 8325
 8326// --- command.amc_js_proc.amc_js.Exec
 8327// Start + Wait
 8328// Execute subprocess and return exit code
 8329int command::amc_js_Exec(command::amc_js_proc& parent) {
 8330    amc_js_Start(parent);
 8331    amc_js_Wait(parent);
 8332    return parent.status;
 8333}
 8334
 8335// --- command.amc_js_proc.amc_js.ExecX
 8336// Start + Wait, throw exception on error
 8337// Execute subprocess; throw human-readable exception on error
 8338void command::amc_js_ExecX(command::amc_js_proc& parent) {
 8339    int rc = amc_js_Exec(parent);
 8340    vrfy(rc==0, tempstr() << "algo_lib.exec" << Keyval("cmd",amc_js_ToCmdline(parent))
 8341    << Keyval("comment",algo::DescribeWaitStatus(parent.status)));
 8342}
 8343
 8344// --- command.amc_js_proc.amc_js.Execv
 8345// Call execv()
 8346// Call execv with specified parameters
 8347int command::amc_js_Execv(command::amc_js_proc& parent) {
 8348    int ret = 0;
 8349    algo::StringAry args;
 8350    amc_js_ToArgv(parent, args);
 8351    char **argv = (char**)alloca((ary_N(args)+1)*sizeof(*argv));
 8352    ind_beg(algo::StringAry_ary_curs,arg,args) {
 8353        argv[ind_curs(arg).index] = Zeroterm(arg);
 8354    }ind_end;
 8355    argv[ary_N(args)] = NULL;
 8356    // if parent.path is relative, search for it in PATH
 8357    algo_lib::ResolveExecFname(parent.path);
 8358    ret = execv(Zeroterm(parent.path),argv);
 8359    return ret;
 8360}
 8361
 8362// --- command.amc_js_proc.amc_js.ToCmdline
 8363algo::tempstr command::amc_js_ToCmdline(command::amc_js_proc& parent) {
 8364    algo::tempstr retval;
 8365    retval << parent.path << " ";
 8366    command::amc_js_PrintArgv(parent.cmd,retval);
 8367    if (ch_N(parent.fstdin)) {
 8368        retval << " " << parent.fstdin;
 8369    }
 8370    if (ch_N(parent.fstdout)) {
 8371        retval << " " << parent.fstdout;
 8372    }
 8373    if (ch_N(parent.fstderr)) {
 8374        retval << " 2" << parent.fstderr;
 8375    }
 8376    return retval;
 8377}
 8378
 8379// --- command.amc_js_proc.amc_js.ToArgv
 8380// Form array from the command line
 8381void command::amc_js_ToArgv(command::amc_js_proc& parent, algo::StringAry& args) {
 8382    ary_RemoveAll(args);
 8383    ary_Alloc(args) << parent.path;
 8384
 8385    if (parent.cmd.in != "data") {
 8386        cstring *arg = &ary_Alloc(args);
 8387        *arg << "-in:";
 8388        cstring_Print(parent.cmd.in, *arg);
 8389    }
 8390    for (int i=1; i < algo_lib::_db.cmdline.verbose; ++i) {
 8391        ary_Alloc(args) << "-verbose";
 8392    }
 8393}
 8394
 8395// --- command.amc_js_proc..Uninit
 8396void command::amc_js_proc_Uninit(command::amc_js_proc& parent) {
 8397    command::amc_js_proc &row = parent; (void)row;
 8398
 8399    // command.amc_js_proc.amc_js.Uninit (Exec)  //
 8400    amc_js_Kill(parent); // kill child, ensure forward progress
 8401}
 8402
 8403// --- command.amc_proc.amc.Start
 8404// Start subprocess
 8405// If subprocess already running, do nothing. Otherwise, start it
 8406int command::amc_Start(command::amc_proc& parent) {
 8407    int retval = 0;
 8408    if (parent.pid == 0) {
 8409        verblog(amc_ToCmdline(parent)); // maybe print command
 8410#ifdef WIN32
 8411        algo_lib::ResolveExecFname(parent.path);
 8412        tempstr cmdline(amc_ToCmdline(parent));
 8413        parent.pid = dospawn(Zeroterm(parent.path),Zeroterm(cmdline),parent.timeout,parent.fstdin,parent.fstdout,parent.fstderr);
 8414#else
 8415        parent.pid = fork();
 8416        if (parent.pid == 0) { // child
 8417            algo_lib::DieWithParent();
 8418            if (parent.timeout > 0) {
 8419                alarm(parent.timeout);
 8420            }
 8421            if (retval==0) retval=algo_lib::ApplyRedirect(parent.fstdin , 0);
 8422            if (retval==0) retval=algo_lib::ApplyRedirect(parent.fstdout, 1);
 8423            if (retval==0) retval=algo_lib::ApplyRedirect(parent.fstderr, 2);
 8424            if (retval==0) retval= amc_Execv(parent);
 8425            if (retval != 0) { // if start fails, print error
 8426                int err=errno;
 8427                prerr("command.amc_execv"
 8428                <<Keyval("errno",err)
 8429                <<Keyval("errstr",strerror(err))
 8430                <<Keyval("comment","Execv failed"));
 8431            }
 8432            _exit(127); // if failed to start, exit anyway
 8433        } else if (parent.pid == -1) {
 8434            retval = errno; // failed to fork
 8435        }
 8436#endif
 8437    }
 8438    parent.status = parent.pid > 0 ? 0 : -1; // if didn't start, set error status
 8439    return retval;
 8440}
 8441
 8442// --- command.amc_proc.amc.StartRead
 8443// Start subprocess & Read output
 8444algo::Fildes command::amc_StartRead(command::amc_proc& parent, algo_lib::FFildes &read) {
 8445    int pipefd[2];
 8446    int rc=pipe(pipefd);
 8447    (void)rc;
 8448    read.fd.value = pipefd[0];
 8449    parent.fstdout  << ">&" << pipefd[1];
 8450    amc_Start(parent);
 8451    (void)close(pipefd[1]);
 8452    return read.fd;
 8453}
 8454
 8455// --- command.amc_proc.amc.Kill
 8456// Kill subprocess and wait
 8457void command::amc_Kill(command::amc_proc& parent) {
 8458    if (parent.pid != 0) {
 8459        kill(parent.pid,9);
 8460        amc_Wait(parent);
 8461    }
 8462}
 8463
 8464// --- command.amc_proc.amc.Wait
 8465// Wait for subprocess to return
 8466void command::amc_Wait(command::amc_proc& parent) {
 8467    if (parent.pid > 0) {
 8468        int wait_flags = 0;
 8469        int wait_status = 0;
 8470        int rc = -1;
 8471        do {
 8472            // really wait for subprocess to exit
 8473            rc = waitpid(parent.pid,&wait_status,wait_flags);
 8474        } while (rc==-1 && errno==EINTR);
 8475        if (rc == parent.pid) {
 8476            parent.status = wait_status;
 8477            parent.pid = 0;
 8478        }
 8479    }
 8480}
 8481
 8482// --- command.amc_proc.amc.Exec
 8483// Start + Wait
 8484// Execute subprocess and return exit code
 8485int command::amc_Exec(command::amc_proc& parent) {
 8486    amc_Start(parent);
 8487    amc_Wait(parent);
 8488    return parent.status;
 8489}
 8490
 8491// --- command.amc_proc.amc.ExecX
 8492// Start + Wait, throw exception on error
 8493// Execute subprocess; throw human-readable exception on error
 8494void command::amc_ExecX(command::amc_proc& parent) {
 8495    int rc = amc_Exec(parent);
 8496    vrfy(rc==0, tempstr() << "algo_lib.exec" << Keyval("cmd",amc_ToCmdline(parent))
 8497    << Keyval("comment",algo::DescribeWaitStatus(parent.status)));
 8498}
 8499
 8500// --- command.amc_proc.amc.Execv
 8501// Call execv()
 8502// Call execv with specified parameters
 8503int command::amc_Execv(command::amc_proc& parent) {
 8504    int ret = 0;
 8505    algo::StringAry args;
 8506    amc_ToArgv(parent, args);
 8507    char **argv = (char**)alloca((ary_N(args)+1)*sizeof(*argv));
 8508    ind_beg(algo::StringAry_ary_curs,arg,args) {
 8509        argv[ind_curs(arg).index] = Zeroterm(arg);
 8510    }ind_end;
 8511    argv[ary_N(args)] = NULL;
 8512    // if parent.path is relative, search for it in PATH
 8513    algo_lib::ResolveExecFname(parent.path);
 8514    ret = execv(Zeroterm(parent.path),argv);
 8515    return ret;
 8516}
 8517
 8518// --- command.amc_proc.amc.ToCmdline
 8519algo::tempstr command::amc_ToCmdline(command::amc_proc& parent) {
 8520    algo::tempstr retval;
 8521    retval << parent.path << " ";
 8522    command::amc_PrintArgv(parent.cmd,retval);
 8523    if (ch_N(parent.fstdin)) {
 8524        retval << " " << parent.fstdin;
 8525    }
 8526    if (ch_N(parent.fstdout)) {
 8527        retval << " " << parent.fstdout;
 8528    }
 8529    if (ch_N(parent.fstderr)) {
 8530        retval << " 2" << parent.fstderr;
 8531    }
 8532    return retval;
 8533}
 8534
 8535// --- command.amc_proc.amc.ToArgv
 8536// Form array from the command line
 8537void command::amc_ToArgv(command::amc_proc& parent, algo::StringAry& args) {
 8538    ary_RemoveAll(args);
 8539    ary_Alloc(args) << parent.path;
 8540
 8541    if (parent.cmd.in_dir != "data") {
 8542        cstring *arg = &ary_Alloc(args);
 8543        *arg << "-in_dir:";
 8544        cstring_Print(parent.cmd.in_dir, *arg);
 8545    }
 8546
 8547    if (parent.cmd.query != "") {
 8548        cstring *arg = &ary_Alloc(args);
 8549        *arg << "-query:";
 8550        cstring_Print(parent.cmd.query, *arg);
 8551    }
 8552
 8553    if (parent.cmd.out_dir != ".") {
 8554        cstring *arg = &ary_Alloc(args);
 8555        *arg << "-out_dir:";
 8556        cstring_Print(parent.cmd.out_dir, *arg);
 8557    }
 8558
 8559    if (parent.cmd.proto != false) {
 8560        cstring *arg = &ary_Alloc(args);
 8561        *arg << "-proto:";
 8562        bool_Print(parent.cmd.proto, *arg);
 8563    }
 8564
 8565    if (parent.cmd.report != true) {
 8566        cstring *arg = &ary_Alloc(args);
 8567        *arg << "-report:";
 8568        bool_Print(parent.cmd.report, *arg);
 8569    }
 8570
 8571    if (parent.cmd.e != false) {
 8572        cstring *arg = &ary_Alloc(args);
 8573        *arg << "-e:";
 8574        bool_Print(parent.cmd.e, *arg);
 8575    }
 8576
 8577    if (parent.cmd.trace.expr != "") {
 8578        cstring *arg = &ary_Alloc(args);
 8579        *arg << "-trace:";
 8580        command::trace_Print(parent.cmd, *arg);
 8581    }
 8582    for (int i=1; i < algo_lib::_db.cmdline.verbose; ++i) {
 8583        ary_Alloc(args) << "-verbose";
 8584    }
 8585}
 8586
 8587// --- command.amc_proc..Uninit
 8588void command::amc_proc_Uninit(command::amc_proc& parent) {
 8589    command::amc_proc &row = parent; (void)row;
 8590
 8591    // command.amc_proc.amc.Uninit (Exec)  //
 8592    amc_Kill(parent); // kill child, ensure forward progress
 8593}
 8594
 8595// --- command.amc_vis.ctype.Print
 8596// Print back to string
 8597void command::ctype_Print(command::amc_vis& parent, algo::cstring &out) {
 8598    Regx_Print(parent.ctype, out);
 8599}
 8600
 8601// --- command.amc_vis.ctype.ReadStrptrMaybe
 8602// Read Regx from string
 8603// Convert string to field. Return success value
 8604bool command::ctype_ReadStrptrMaybe(command::amc_vis& parent, algo::strptr in) {
 8605    bool retval = true;
 8606    Regx_ReadSql(parent.ctype, in, true);
 8607    return retval;
 8608}
 8609
 8610// --- command.amc_vis..ReadFieldMaybe
 8611bool command::amc_vis_ReadFieldMaybe(command::amc_vis& parent, algo::strptr field, algo::strptr strval) {
 8612    bool retval = true;
 8613    command::FieldId field_id;
 8614    (void)value_SetStrptrMaybe(field_id,field);
 8615    switch(field_id) {
 8616        case command_FieldId_ctype: {
 8617            retval = ctype_ReadStrptrMaybe(parent, strval);
 8618            break;
 8619        }
 8620        case command_FieldId_in: {
 8621            retval = algo::cstring_ReadStrptrMaybe(parent.in, strval);
 8622            break;
 8623        }
 8624        case command_FieldId_dot: {
 8625            retval = algo::cstring_ReadStrptrMaybe(parent.dot, strval);
 8626            break;
 8627        }
 8628        case command_FieldId_xref: {
 8629            retval = bool_ReadStrptrMaybe(parent.xref, strval);
 8630            break;
 8631        }
 8632        case command_FieldId_xns: {
 8633            retval = bool_ReadStrptrMaybe(parent.xns, strval);
 8634            break;
 8635        }
 8636        case command_FieldId_noinput: {
 8637            retval = bool_ReadStrptrMaybe(parent.noinput, strval);
 8638            break;
 8639        }
 8640        case command_FieldId_check: {
 8641            retval = bool_ReadStrptrMaybe(parent.check, strval);
 8642            break;
 8643        }
 8644        case command_FieldId_render: {
 8645            retval = bool_ReadStrptrMaybe(parent.render, strval);
 8646            break;
 8647        }
 8648        default: break;
 8649    }
 8650    if (!retval) {
 8651        algo_lib::AppendErrtext("attr",field);
 8652    }
 8653    return retval;
 8654}
 8655
 8656// --- command.amc_vis..ReadTupleMaybe
 8657// Read fields of command::amc_vis from attributes of ascii tuple TUPLE
 8658bool command::amc_vis_ReadTupleMaybe(command::amc_vis &parent, algo::Tuple &tuple) {
 8659    bool retval = true;
 8660    int anon_idx = 0;
 8661    ind_beg(algo::Tuple_attrs_curs,attr,tuple) {
 8662        if (ch_N(attr.name) == 0) {
 8663            attr.name = amc_vis_GetAnon(parent, anon_idx++);
 8664        }
 8665        retval = amc_vis_ReadFieldMaybe(parent, attr.name, attr.value);
 8666        if (!retval) {
 8667            break;
 8668        }
 8669    }ind_end;
 8670    return retval;
 8671}
 8672
 8673// --- command.amc_vis..Init
 8674// Set all fields to initial values.
 8675void command::amc_vis_Init(command::amc_vis& parent) {
 8676    Regx_ReadSql(parent.ctype, "%", true);
 8677    parent.in = algo::strptr("data");
 8678    parent.dot = algo::strptr("");
 8679    parent.xref = bool(false);
 8680    parent.xns = bool(false);
 8681    parent.noinput = bool(false);
 8682    parent.check = bool(false);
 8683    parent.render = bool(true);
 8684}
 8685
 8686// --- command.amc_vis..ToCmdline
 8687// Convenience function that returns a full command line
 8688// Assume command is in a directory called bin
 8689tempstr command::amc_vis_ToCmdline(command::amc_vis& row) {
 8690    tempstr ret;
 8691    ret << "bin/amc_vis ";
 8692    amc_vis_PrintArgv(row, ret);
 8693    // inherit less intense verbose, debug options
 8694    for (int i = 1; i < algo_lib::_db.cmdline.verbose; i++) {
 8695        ret << " -verbose";
 8696    }
 8697    for (int i = 1; i < algo_lib::_db.cmdline.debug; i++) {
 8698        ret << " -debug";
 8699    }
 8700    return ret;
 8701}
 8702
 8703// --- command.amc_vis..PrintArgv
 8704// print string representation of ROW to string STR
 8705// cfmt:command.amc_vis.Argv  printfmt:Auto
 8706void command::amc_vis_PrintArgv(command::amc_vis& row, algo::cstring& str) {
 8707    algo::tempstr temp;
 8708    (void)temp;
 8709    (void)str;
 8710    ch_RemoveAll(temp);
 8711    command::ctype_Print(const_cast<command::amc_vis&>(row), temp);
 8712    str << " -ctype:";
 8713    strptr_PrintBash(temp,str);
 8714    if (!(row.in == "data")) {
 8715        ch_RemoveAll(temp);
 8716        cstring_Print(row.in, temp);
 8717        str << " -in:";
 8718        strptr_PrintBash(temp,str);
 8719    }
 8720    if (!(row.dot == "")) {
 8721        ch_RemoveAll(temp);
 8722        cstring_Print(row.dot, temp);
 8723        str << " -dot:";
 8724        strptr_PrintBash(temp,str);
 8725    }
 8726    if (!(row.xref == false)) {
 8727        ch_RemoveAll(temp);
 8728        bool_Print(row.xref, temp);
 8729        str << " -xref:";
 8730        strptr_PrintBash(temp,str);
 8731    }
 8732    if (!(row.xns == false)) {
 8733        ch_RemoveAll(temp);
 8734        bool_Print(row.xns, temp);
 8735        str << " -xns:";
 8736        strptr_PrintBash(temp,str);
 8737    }
 8738    if (!(row.noinput == false)) {
 8739        ch_RemoveAll(temp);
 8740        bool_Print(row.noinput, temp);
 8741        str << " -noinput:";
 8742        strptr_PrintBash(temp,str);
 8743    }
 8744    if (!(row.check == false)) {
 8745        ch_RemoveAll(temp);
 8746        bool_Print(row.check, temp);
 8747        str << " -check:";
 8748        strptr_PrintBash(temp,str);
 8749    }
 8750    if (!(row.render == true)) {
 8751        ch_RemoveAll(temp);
 8752        bool_Print(row.render, temp);
 8753        str << " -render:";
 8754        strptr_PrintBash(temp,str);
 8755    }
 8756}
 8757
 8758// --- command.amc_vis..GetAnon
 8759algo::strptr command::amc_vis_GetAnon(command::amc_vis &parent, i32 idx) {
 8760    (void)parent;//only to avoid -Wunused-parameter
 8761    switch(idx) {
 8762        case(0): return strptr("ctype", 5);
 8763        default: return algo::strptr();
 8764    }
 8765}
 8766
 8767// --- command.amc_vis..NArgs
 8768// Used with command lines
 8769// Return # of command-line arguments that must follow this argument
 8770// If FIELD is invalid, return -1
 8771i32 command::amc_vis_NArgs(command::FieldId field, algo::strptr& out_dflt, bool* out_anon) {
 8772    i32 retval = 1;
 8773    switch (field) {
 8774        case command_FieldId_ctype: { // $comment
 8775            *out_anon = true;
 8776        } break;
 8777        case command_FieldId_in: { // $comment
 8778            *out_anon = false;
 8779        } break;
 8780        case command_FieldId_dot: { // $comment
 8781            *out_anon = false;
 8782        } break;
 8783        case command_FieldId_xref: { // $comment
 8784            *out_anon = false;
 8785            retval=0;
 8786            out_dflt="Y";
 8787        } break;
 8788        case command_FieldId_xns: { // bool: no argument required but value may be specified as xref:Y
 8789            *out_anon = false;
 8790            retval=0;
 8791            out_dflt="Y";
 8792        } break;
 8793        case command_FieldId_noinput: { // bool: no argument required but value may be specified as xns:Y
 8794            *out_anon = false;
 8795            retval=0;
 8796            out_dflt="Y";
 8797        } break;
 8798        case command_FieldId_check: { // bool: no argument required but value may be specified as noinput:Y
 8799            *out_anon = false;
 8800            retval=0;
 8801            out_dflt="Y";
 8802        } break;
 8803        case command_FieldId_render: { // bool: no argument required but value may be specified as check:Y
 8804            *out_anon = false;
 8805            retval=0;
 8806            out_dflt="Y";
 8807        } break;
 8808        default:
 8809        retval=-1; // unrecognized
 8810    }
 8811    return retval;
 8812}
 8813
 8814// --- command.amc_vis_proc.amc_vis.Start
 8815// Start subprocess
 8816// If subprocess already running, do nothing. Otherwise, start it
 8817int command::amc_vis_Start(command::amc_vis_proc& parent) {
 8818    int retval = 0;
 8819    if (parent.pid == 0) {
 8820        verblog(amc_vis_ToCmdline(parent)); // maybe print command
 8821#ifdef WIN32
 8822        algo_lib::ResolveExecFname(parent.path);
 8823        tempstr cmdline(amc_vis_ToCmdline(parent));
 8824        parent.pid = dospawn(Zeroterm(parent.path),Zeroterm(cmdline),parent.timeout,parent.fstdin,parent.fstdout,parent.fstderr);
 8825#else
 8826        parent.pid = fork();
 8827        if (parent.pid == 0) { // child
 8828            algo_lib::DieWithParent();
 8829            if (parent.timeout > 0) {
 8830                alarm(parent.timeout);
 8831            }
 8832            if (retval==0) retval=algo_lib::ApplyRedirect(parent.fstdin , 0);
 8833            if (retval==0) retval=algo_lib::ApplyRedirect(parent.fstdout, 1);
 8834            if (retval==0) retval=algo_lib::ApplyRedirect(parent.fstderr, 2);
 8835            if (retval==0) retval= amc_vis_Execv(parent);
 8836            if (retval != 0) { // if start fails, print error
 8837                int err=errno;
 8838                prerr("command.amc_vis_execv"
 8839                <<Keyval("errno",err)
 8840                <<Keyval("errstr",strerror(err))
 8841                <<Keyval("comment","Execv failed"));
 8842            }
 8843            _exit(127); // if failed to start, exit anyway
 8844        } else if (parent.pid == -1) {
 8845            retval = errno; // failed to fork
 8846        }
 8847#endif
 8848    }
 8849    parent.status = parent.pid > 0 ? 0 : -1; // if didn't start, set error status
 8850    return retval;
 8851}
 8852
 8853// --- command.amc_vis_proc.amc_vis.StartRead
 8854// Start subprocess & Read output
 8855algo::Fildes command::amc_vis_StartRead(command::amc_vis_proc& parent, algo_lib::FFildes &read) {
 8856    int pipefd[2];
 8857    int rc=pipe(pipefd);
 8858    (void)rc;
 8859    read.fd.value = pipefd[0];
 8860    parent.fstdout  << ">&" << pipefd[1];
 8861    amc_vis_Start(parent);
 8862    (void)close(pipefd[1]);
 8863    return read.fd;
 8864}
 8865
 8866// --- command.amc_vis_proc.amc_vis.Kill
 8867// Kill subprocess and wait
 8868void command::amc_vis_Kill(command::amc_vis_proc& parent) {
 8869    if (parent.pid != 0) {
 8870        kill(parent.pid,9);
 8871        amc_vis_Wait(parent);
 8872    }
 8873}
 8874
 8875// --- command.amc_vis_proc.amc_vis.Wait
 8876// Wait for subprocess to return
 8877void command::amc_vis_Wait(command::amc_vis_proc& parent) {
 8878    if (parent.pid > 0) {
 8879        int wait_flags = 0;
 8880        int wait_status = 0;
 8881        int rc = -1;
 8882        do {
 8883            // really wait for subprocess to exit
 8884            rc = waitpid(parent.pid,&wait_status,wait_flags);
 8885        } while (rc==-1 && errno==EINTR);
 8886        if (rc == parent.pid) {
 8887            parent.status = wait_status;
 8888            parent.pid = 0;
 8889        }
 8890    }
 8891}
 8892
 8893// --- command.amc_vis_proc.amc_vis.Exec
 8894// Start + Wait
 8895// Execute subprocess and return exit code
 8896int command::amc_vis_Exec(command::amc_vis_proc& parent) {
 8897    amc_vis_Start(parent);
 8898    amc_vis_Wait(parent);
 8899    return parent.status;
 8900}
 8901
 8902// --- command.amc_vis_proc.amc_vis.ExecX
 8903// Start + Wait, throw exception on error
 8904// Execute subprocess; throw human-readable exception on error
 8905void command::amc_vis_ExecX(command::amc_vis_proc& parent) {
 8906    int rc = amc_vis_Exec(parent);
 8907    vrfy(rc==0, tempstr() << "algo_lib.exec" << Keyval("cmd",amc_vis_ToCmdline(parent))
 8908    << Keyval("comment",algo::DescribeWaitStatus(parent.status)));
 8909}
 8910
 8911// --- command.amc_vis_proc.amc_vis.Execv
 8912// Call execv()
 8913// Call execv with specified parameters
 8914int command::amc_vis_Execv(command::amc_vis_proc& parent) {
 8915    int ret = 0;
 8916    algo::StringAry args;
 8917    amc_vis_ToArgv(parent, args);
 8918    char **argv = (char**)alloca((ary_N(args)+1)*sizeof(*argv));
 8919    ind_beg(algo::StringAry_ary_curs,arg,args) {
 8920        argv[ind_curs(arg).index] = Zeroterm(arg);
 8921    }ind_end;
 8922    argv[ary_N(args)] = NULL;
 8923    // if parent.path is relative, search for it in PATH
 8924    algo_lib::ResolveExecFname(parent.path);
 8925    ret = execv(Zeroterm(parent.path),argv);
 8926    return ret;
 8927}
 8928
 8929// --- command.amc_vis_proc.amc_vis.ToCmdline
 8930algo::tempstr command::amc_vis_ToCmdline(command::amc_vis_proc& parent) {
 8931    algo::tempstr retval;
 8932    retval << parent.path << " ";
 8933    command::amc_vis_PrintArgv(parent.cmd,retval);
 8934    if (ch_N(parent.fstdin)) {
 8935        retval << " " << parent.fstdin;
 8936    }
 8937    if (ch_N(parent.fstdout)) {
 8938        retval << " " << parent.fstdout;
 8939    }
 8940    if (ch_N(parent.fstderr)) {
 8941        retval << " 2" << parent.fstderr;
 8942    }
 8943    return retval;
 8944}
 8945
 8946// --- command.amc_vis_proc.amc_vis.ToArgv
 8947// Form array from the command line
 8948void command::amc_vis_ToArgv(command::amc_vis_proc& parent, algo::StringAry& args) {
 8949    ary_RemoveAll(args);
 8950    ary_Alloc(args) << parent.path;
 8951
 8952    if (parent.cmd.ctype.expr != "%") {
 8953        cstring *arg = &ary_Alloc(args);
 8954        *arg << "-ctype:";
 8955        command::ctype_Print(parent.cmd, *arg);
 8956    }
 8957
 8958    if (parent.cmd.in != "data") {
 8959        cstring *arg = &ary_Alloc(args);
 8960        *arg << "-in:";
 8961        cstring_Print(parent.cmd.in, *arg);
 8962    }
 8963
 8964    if (parent.cmd.dot != "") {
 8965        cstring *arg = &ary_Alloc(args);
 8966        *arg << "-dot:";
 8967        cstring_Print(parent.cmd.dot, *arg);
 8968    }
 8969
 8970    if (parent.cmd.xref != false) {
 8971        cstring *arg = &ary_Alloc(args);
 8972        *arg << "-xref:";
 8973        bool_Print(parent.cmd.xref, *arg);
 8974    }
 8975
 8976    if (parent.cmd.xns != false) {
 8977        cstring *arg = &ary_Alloc(args);
 8978        *arg << "-xns:";
 8979        bool_Print(parent.cmd.xns, *arg);
 8980    }
 8981
 8982    if (parent.cmd.noinput != false) {
 8983        cstring *arg = &ary_Alloc(args);
 8984        *arg << "-noinput:";
 8985        bool_Print(parent.cmd.noinput, *arg);
 8986    }
 8987
 8988    if (parent.cmd.check != false) {
 8989        cstring *arg = &ary_Alloc(args);
 8990        *arg << "-check:";
 8991        bool_Print(parent.cmd.check, *arg);
 8992    }
 8993
 8994    if (parent.cmd.render != true) {
 8995        cstring *arg = &ary_Alloc(args);
 8996        *arg << "-render:";
 8997        bool_Print(parent.cmd.render, *arg);
 8998    }
 8999    for (int i=1; i < algo_lib::_db.cmdline.verbose; ++i) {
 9000        ary_Alloc(args) << "-verbose";
 9001    }
 9002}
 9003
 9004// --- command.amc_vis_proc..Uninit
 9005void command::amc_vis_proc_Uninit(command::amc_vis_proc& parent) {
 9006    command::amc_vis_proc &row = parent; (void)row;
 9007
 9008    // command.amc_vis_proc.amc_vis.Uninit (Exec)  //
 9009    amc_vis_Kill(parent); // kill child, ensure forward progress
 9010}
 9011
 9012// --- command.ams_cat..ReadFieldMaybe
 9013bool command::ams_cat_ReadFieldMaybe(command::ams_cat& parent, algo::strptr field, algo::strptr strval) {
 9014    bool retval = true;
 9015    command::FieldId field_id;
 9016    (void)value_SetStrptrMaybe(field_id,field);
 9017    switch(field_id) {
 9018        case command_FieldId_in: {
 9019            retval = algo::cstring_ReadStrptrMaybe(parent.in, strval);
 9020            break;
 9021        }
 9022        default: break;
 9023    }
 9024    if (!retval) {
 9025        algo_lib::AppendErrtext("attr",field);
 9026    }
 9027    return retval;
 9028}
 9029
 9030// --- command.ams_cat..ReadTupleMaybe
 9031// Read fields of command::ams_cat from attributes of ascii tuple TUPLE
 9032bool command::ams_cat_ReadTupleMaybe(command::ams_cat &parent, algo::Tuple &tuple) {
 9033    bool retval = true;
 9034    ind_beg(algo::Tuple_attrs_curs,attr,tuple) {
 9035        retval = ams_cat_ReadFieldMaybe(parent, attr.name, attr.value);
 9036        if (!retval) {
 9037            break;
 9038        }
 9039    }ind_end;
 9040    return retval;
 9041}
 9042
 9043// --- command.ams_cat..ToCmdline
 9044// Convenience function that returns a full command line
 9045// Assume command is in a directory called bin
 9046tempstr command::ams_cat_ToCmdline(command::ams_cat& row) {
 9047    tempstr ret;
 9048    ret << "bin/ams_cat ";
 9049    ams_cat_PrintArgv(row, ret);
 9050    // inherit less intense verbose, debug options
 9051    for (int i = 1; i < algo_lib::_db.cmdline.verbose; i++) {
 9052        ret << " -verbose";
 9053    }
 9054    for (int i = 1; i < algo_lib::_db.cmdline.debug; i++) {
 9055        ret << " -debug";
 9056    }
 9057    return ret;
 9058}
 9059
 9060// --- command.ams_cat..PrintArgv
 9061// print string representation of ROW to string STR
 9062// cfmt:command.ams_cat.Argv  printfmt:Tuple
 9063void command::ams_cat_PrintArgv(command::ams_cat& row, algo::cstring& str) {
 9064    algo::tempstr temp;
 9065    (void)temp;
 9066    (void)str;
 9067    if (!(row.in == "data")) {
 9068        ch_RemoveAll(temp);
 9069        cstring_Print(row.in, temp);
 9070        str << " -in:";
 9071        strptr_PrintBash(temp,str);
 9072    }
 9073}
 9074
 9075// --- command.ams_cat..NArgs
 9076// Used with command lines
 9077// Return # of command-line arguments that must follow this argument
 9078// If FIELD is invalid, return -1
 9079i32 command::ams_cat_NArgs(command::FieldId field, algo::strptr& out_dflt, bool* out_anon) {
 9080    i32 retval = 1;
 9081    switch (field) {
 9082        case command_FieldId_in: { // $comment
 9083            *out_anon = false;
 9084        } break;
 9085        default:
 9086        retval=-1; // unrecognized
 9087    }
 9088    (void)out_dflt;//only to avoid -Wunused-parameter
 9089    return retval;
 9090}
 9091
 9092// --- command.ams_cat_proc.ams_cat.Start
 9093// Start subprocess
 9094// If subprocess already running, do nothing. Otherwise, start it
 9095int command::ams_cat_Start(command::ams_cat_proc& parent) {
 9096    int retval = 0;
 9097    if (parent.pid == 0) {
 9098        verblog(ams_cat_ToCmdline(parent)); // maybe print command
 9099#ifdef WIN32
 9100        algo_lib::ResolveExecFname(parent.path);
 9101        tempstr cmdline(ams_cat_ToCmdline(parent));
 9102        parent.pid = dospawn(Zeroterm(parent.path),Zeroterm(cmdline),parent.timeout,parent.fstdin,parent.fstdout,parent.fstderr);
 9103#else
 9104        parent.pid = fork();
 9105        if (parent.pid == 0) { // child
 9106            algo_lib::DieWithParent();
 9107            if (parent.timeout > 0) {
 9108                alarm(parent.timeout);
 9109            }
 9110            if (retval==0) retval=algo_lib::ApplyRedirect(parent.fstdin , 0);
 9111            if (retval==0) retval=algo_lib::ApplyRedirect(parent.fstdout, 1);
 9112            if (retval==0) retval=algo_lib::ApplyRedirect(parent.fstderr, 2);
 9113            if (retval==0) retval= ams_cat_Execv(parent);
 9114            if (retval != 0) { // if start fails, print error
 9115                int err=errno;
 9116                prerr("command.ams_cat_execv"
 9117                <<Keyval("errno",err)
 9118                <<Keyval("errstr",strerror(err))
 9119                <<Keyval("comment","Execv failed"));
 9120            }
 9121            _exit(127); // if failed to start, exit anyway
 9122        } else if (parent.pid == -1) {
 9123            retval = errno; // failed to fork
 9124        }
 9125#endif
 9126    }
 9127    parent.status = parent.pid > 0 ? 0 : -1; // if didn't start, set error status
 9128    return retval;
 9129}
 9130
 9131// --- command.ams_cat_proc.ams_cat.StartRead
 9132// Start subprocess & Read output
 9133algo::Fildes command::ams_cat_StartRead(command::ams_cat_proc& parent, algo_lib::FFildes &read) {
 9134    int pipefd[2];
 9135    int rc=pipe(pipefd);
 9136    (void)rc;
 9137    read.fd.value = pipefd[0];
 9138    parent.fstdout  << ">&" << pipefd[1];
 9139    ams_cat_Start(parent);
 9140    (void)close(pipefd[1]);
 9141    return read.fd;
 9142}
 9143
 9144// --- command.ams_cat_proc.ams_cat.Kill
 9145// Kill subprocess and wait
 9146void command::ams_cat_Kill(command::ams_cat_proc& parent) {
 9147    if (parent.pid != 0) {
 9148        kill(parent.pid,9);
 9149        ams_cat_Wait(parent);
 9150    }
 9151}
 9152
 9153// --- command.ams_cat_proc.ams_cat.Wait
 9154// Wait for subprocess to return
 9155void command::ams_cat_Wait(command::ams_cat_proc& parent) {
 9156    if (parent.pid > 0) {
 9157        int wait_flags = 0;
 9158        int wait_status = 0;
 9159        int rc = -1;
 9160        do {
 9161            // really wait for subprocess to exit
 9162            rc = waitpid(parent.pid,&wait_status,wait_flags);
 9163        } while (rc==-1 && errno==EINTR);
 9164        if (rc == parent.pid) {
 9165            parent.status = wait_status;
 9166            parent.pid = 0;
 9167        }
 9168    }
 9169}
 9170
 9171// --- command.ams_cat_proc.ams_cat.Exec
 9172// Start + Wait
 9173// Execute subprocess and return exit code
 9174int command::ams_cat_Exec(command::ams_cat_proc& parent) {
 9175    ams_cat_Start(parent);
 9176    ams_cat_Wait(parent);
 9177    return parent.status;
 9178}
 9179
 9180// --- command.ams_cat_proc.ams_cat.ExecX
 9181// Start + Wait, throw exception on error
 9182// Execute subprocess; throw human-readable exception on error
 9183void command::ams_cat_ExecX(command::ams_cat_proc& parent) {
 9184    int rc = ams_cat_Exec(parent);
 9185    vrfy(rc==0, tempstr() << "algo_lib.exec" << Keyval("cmd",ams_cat_ToCmdline(parent))
 9186    << Keyval("comment",algo::DescribeWaitStatus(parent.status)));
 9187}
 9188
 9189// --- command.ams_cat_proc.ams_cat.Execv
 9190// Call execv()
 9191// Call execv with specified parameters
 9192int command::ams_cat_Execv(command::ams_cat_proc& parent) {
 9193    int ret = 0;
 9194    algo::StringAry args;
 9195    ams_cat_ToArgv(parent, args);
 9196    char **argv = (char**)alloca((ary_N(args)+1)*sizeof(*argv));
 9197    ind_beg(algo::StringAry_ary_curs,arg,args) {
 9198        argv[ind_curs(arg).index] = Zeroterm(arg);
 9199    }ind_end;
 9200    argv[ary_N(args)] = NULL;
 9201    // if parent.path is relative, search for it in PATH
 9202    algo_lib::ResolveExecFname(parent.path);
 9203    ret = execv(Zeroterm(parent.path),argv);
 9204    return ret;
 9205}
 9206
 9207// --- command.ams_cat_proc.ams_cat.ToCmdline
 9208algo::tempstr command::ams_cat_ToCmdline(command::ams_cat_proc& parent) {
 9209    algo::tempstr retval;
 9210    retval << parent.path << " ";
 9211    command::ams_cat_PrintArgv(parent.cmd,retval);
 9212    if (ch_N(parent.fstdin)) {
 9213        retval << " " << parent.fstdin;
 9214    }
 9215    if (ch_N(parent.fstdout)) {
 9216        retval << " " << parent.fstdout;
 9217    }
 9218    if (ch_N(parent.fstderr)) {
 9219        retval << " 2" << parent.fstderr;
 9220    }
 9221    return retval;
 9222}
 9223
 9224// --- command.ams_cat_proc.ams_cat.ToArgv
 9225// Form array from the command line
 9226void command::ams_cat_ToArgv(command::ams_cat_proc& parent, algo::StringAry& args) {
 9227    ary_RemoveAll(args);
 9228    ary_Alloc(args) << parent.path;
 9229
 9230    if (parent.cmd.in != "data") {
 9231        cstring *arg = &ary_Alloc(args);
 9232        *arg << "-in:";
 9233        cstring_Print(parent.cmd.in, *arg);
 9234    }
 9235    for (int i=1; i < algo_lib::_db.cmdline.verbose; ++i) {
 9236        ary_Alloc(args) << "-verbose";
 9237    }
 9238}
 9239
 9240// --- command.ams_cat_proc..Uninit
 9241void command::ams_cat_proc_Uninit(command::ams_cat_proc& parent) {
 9242    command::ams_cat_proc &row = parent; (void)row;
 9243
 9244    // command.ams_cat_proc.ams_cat.Uninit (Exec)  //
 9245    ams_cat_Kill(parent); // kill child, ensure forward progress
 9246}
 9247
 9248// --- command.ams_sendtest.trace.Print
 9249// Print back to string
 9250void command::trace_Print(command::ams_sendtest& parent, algo::cstring &out) {
 9251    Regx_Print(parent.trace, out);
 9252}
 9253
 9254// --- command.ams_sendtest.trace.ReadStrptrMaybe
 9255// Read Regx from string
 9256// Convert string to field. Return success value
 9257bool command::trace_ReadStrptrMaybe(command::ams_sendtest& parent, algo::strptr in) {
 9258    bool retval = true;
 9259    Regx_ReadSql(parent.trace, in, true);
 9260    return retval;
 9261}
 9262
 9263// --- command.ams_sendtest..ReadFieldMaybe
 9264bool command::ams_sendtest_ReadFieldMaybe(command::ams_sendtest& parent, algo::strptr field, algo::strptr strval) {
 9265    bool retval = true;
 9266    command::FieldId field_id;
 9267    (void)value_SetStrptrMaybe(field_id,field);
 9268    switch(field_id) {
 9269        case command_FieldId_in: {
 9270            retval = algo::cstring_ReadStrptrMaybe(parent.in, strval);
 9271            break;
 9272        }
 9273        case command_FieldId_id: {
 9274            retval = i32_ReadStrptrMaybe(parent.id, strval);
 9275            break;
 9276        }
 9277        case command_FieldId_file_prefix: {
 9278            retval = algo::cstring_ReadStrptrMaybe(parent.file_prefix, strval);
 9279            break;
 9280        }
 9281        case command_FieldId_nchild: {
 9282            retval = i32_ReadStrptrMaybe(parent.nchild, strval);
 9283            break;
 9284        }
 9285        case command_FieldId_blocking: {
 9286            retval = bool_ReadStrptrMaybe(parent.blocking, strval);
 9287            break;
 9288        }
 9289        case command_FieldId_nmsg: {
 9290            retval = i32_ReadStrptrMaybe(parent.nmsg, strval);
 9291            break;
 9292        }
 9293        case command_FieldId_trace: {
 9294            retval = trace_ReadStrptrMaybe(parent, strval);
 9295            break;
 9296        }
 9297        case command_FieldId_timeout: {
 9298            retval = i32_ReadStrptrMaybe(parent.timeout, strval);
 9299            break;
 9300        }
 9301        case command_FieldId_recvdelay_ns: {
 9302            retval = i64_ReadStrptrMaybe(parent.recvdelay_ns, strval);
 9303            break;
 9304        }
 9305        case command_FieldId_senddelay_ns: {
 9306            retval = i64_ReadStrptrMaybe(parent.senddelay_ns, strval);
 9307            break;
 9308        }
 9309        case command_FieldId_msgsize_min: {
 9310            retval = i32_ReadStrptrMaybe(parent.msgsize_min, strval);
 9311            break;
 9312        }
 9313        case command_FieldId_msgsize_max: {
 9314            retval = i32_ReadStrptrMaybe(parent.msgsize_max, strval);
 9315            break;
 9316        }
 9317        case command_FieldId_bufsize: {
 9318            retval = i32_ReadStrptrMaybe(parent.bufsize, strval);
 9319            break;
 9320        }
 9321        case command_FieldId_recvdelay: {
 9322            retval = i64_ReadStrptrMaybe(parent.recvdelay, strval);
 9323            break;
 9324        }
 9325        default: break;
 9326    }
 9327    if (!retval) {
 9328        algo_lib::AppendErrtext("attr",field);
 9329    }
 9330    return retval;
 9331}
 9332
 9333// --- command.ams_sendtest..ReadTupleMaybe
 9334// Read fields of command::ams_sendtest from attributes of ascii tuple TUPLE
 9335bool command::ams_sendtest_ReadTupleMaybe(command::ams_sendtest &parent, algo::Tuple &tuple) {
 9336    bool retval = true;
 9337    ind_beg(algo::Tuple_attrs_curs,attr,tuple) {
 9338        retval = ams_sendtest_ReadFieldMaybe(parent, attr.name, attr.value);
 9339        if (!retval) {
 9340            break;
 9341        }
 9342    }ind_end;
 9343    return retval;
 9344}
 9345
 9346// --- command.ams_sendtest..Init
 9347// Set all fields to initial values.
 9348void command::ams_sendtest_Init(command::ams_sendtest& parent) {
 9349    parent.in = algo::strptr("data");
 9350    parent.id = i32(0);
 9351    parent.file_prefix = algo::strptr("");
 9352    parent.nchild = i32(1);
 9353    parent.blocking = bool(false);
 9354    parent.nmsg = i32(1000);
 9355    Regx_ReadSql(parent.trace, "", true);
 9356    parent.timeout = i32(30);
 9357    parent.recvdelay_ns = i64(0);
 9358    parent.senddelay_ns = i64(0);
 9359    parent.msgsize_min = i32(64);
 9360    parent.msgsize_max = i32(1024);
 9361    parent.bufsize = i32(32768);
 9362    parent.recvdelay = i64(0);
 9363}
 9364
 9365// --- command.ams_sendtest..ToCmdline
 9366// Convenience function that returns a full command line
 9367// Assume command is in a directory called bin
 9368tempstr command::ams_sendtest_ToCmdline(command::ams_sendtest& row) {
 9369    tempstr ret;
 9370    ret << "bin/ams_sendtest ";
 9371    ams_sendtest_PrintArgv(row, ret);
 9372    // inherit less intense verbose, debug options
 9373    for (int i = 1; i < algo_lib::_db.cmdline.verbose; i++) {
 9374        ret << " -verbose";
 9375    }
 9376    for (int i = 1; i < algo_lib::_db.cmdline.debug; i++) {
 9377        ret << " -debug";
 9378    }
 9379    return ret;
 9380}
 9381
 9382// --- command.ams_sendtest..PrintArgv
 9383// print string representation of ROW to string STR
 9384// cfmt:command.ams_sendtest.Argv  printfmt:Tuple
 9385void command::ams_sendtest_PrintArgv(command::ams_sendtest& row, algo::cstring& str) {
 9386    algo::tempstr temp;
 9387    (void)temp;
 9388    (void)str;
 9389    if (!(row.in == "data")) {
 9390        ch_RemoveAll(temp);
 9391        cstring_Print(row.in, temp);
 9392        str << " -in:";
 9393        strptr_PrintBash(temp,str);
 9394    }
 9395    if (!(row.id == 0)) {
 9396        ch_RemoveAll(temp);
 9397        i32_Print(row.id, temp);
 9398        str << " -id:";
 9399        strptr_PrintBash(temp,str);
 9400    }
 9401    if (!(row.file_prefix == "")) {
 9402        ch_RemoveAll(temp);
 9403        cstring_Print(row.file_prefix, temp);
 9404        str << " -file_prefix:";
 9405        strptr_PrintBash(temp,str);
 9406    }
 9407    if (!(row.nchild == 1)) {
 9408        ch_RemoveAll(temp);
 9409        i32_Print(row.nchild, temp);
 9410        str << " -nchild:";
 9411        strptr_PrintBash(temp,str);
 9412    }
 9413    if (!(row.blocking == false)) {
 9414        ch_RemoveAll(temp);
 9415        bool_Print(row.blocking, temp);
 9416        str << " -blocking:";
 9417        strptr_PrintBash(temp,str);
 9418    }
 9419    if (!(row.nmsg == 1000)) {
 9420        ch_RemoveAll(temp);
 9421        i32_Print(row.nmsg, temp);
 9422        str << " -nmsg:";
 9423        strptr_PrintBash(temp,str);
 9424    }
 9425    if (!(row.trace.expr == "")) {
 9426        ch_RemoveAll(temp);
 9427        command::trace_Print(const_cast<command::ams_sendtest&>(row), temp);
 9428        str << " -trace:";
 9429        strptr_PrintBash(temp,str);
 9430    }
 9431    if (!(row.timeout == 30)) {
 9432        ch_RemoveAll(temp);
 9433        i32_Print(row.timeout, temp);
 9434        str << " -timeout:";
 9435        strptr_PrintBash(temp,str);
 9436    }
 9437    if (!(row.recvdelay_ns == 0)) {
 9438        ch_RemoveAll(temp);
 9439        i64_Print(row.recvdelay_ns, temp);
 9440        str << " -recvdelay_ns:";
 9441        strptr_PrintBash(temp,str);
 9442    }
 9443    if (!(row.senddelay_ns == 0)) {
 9444        ch_RemoveAll(temp);
 9445        i64_Print(row.senddelay_ns, temp);
 9446        str << " -senddelay_ns:";
 9447        strptr_PrintBash(temp,str);
 9448    }
 9449    if (!(row.msgsize_min == 64)) {
 9450        ch_RemoveAll(temp);
 9451        i32_Print(row.msgsize_min, temp);
 9452        str << " -msgsize_min:";
 9453        strptr_PrintBash(temp,str);
 9454    }
 9455    if (!(row.msgsize_max == 1024)) {
 9456        ch_RemoveAll(temp);
 9457        i32_Print(row.msgsize_max, temp);
 9458        str << " -msgsize_max:";
 9459        strptr_PrintBash(temp,str);
 9460    }
 9461    if (!(row.bufsize == 32768)) {
 9462        ch_RemoveAll(temp);
 9463        i32_Print(row.bufsize, temp);
 9464        str << " -bufsize:";
 9465        strptr_PrintBash(temp,str);
 9466    }
 9467    if (!(row.recvdelay == 0)) {
 9468        ch_RemoveAll(temp);
 9469        i64_Print(row.recvdelay, temp);
 9470        str << " -recvdelay:";
 9471        strptr_PrintBash(temp,str);
 9472    }
 9473}
 9474
 9475// --- command.ams_sendtest..NArgs
 9476// Used with command lines
 9477// Return # of command-line arguments that must follow this argument
 9478// If FIELD is invalid, return -1
 9479i32 command::ams_sendtest_NArgs(command::FieldId field, algo::strptr& out_dflt, bool* out_anon) {
 9480    i32 retval = 1;
 9481    switch (field) {
 9482        case command_FieldId_in: { // $comment
 9483            *out_anon = false;
 9484        } break;
 9485        case command_FieldId_id: { // $comment
 9486            *out_anon = false;
 9487        } break;
 9488        case command_FieldId_file_prefix: { // $comment
 9489            *out_anon = false;
 9490        } break;
 9491        case command_FieldId_nchild: { // $comment
 9492            *out_anon = false;
 9493        } break;
 9494        case command_FieldId_blocking: { // $comment
 9495            *out_anon = false;
 9496            retval=0;
 9497            out_dflt="Y";
 9498        } break;
 9499        case command_FieldId_nmsg: { // bool: no argument required but value may be specified as blocking:Y
 9500            *out_anon = false;
 9501        } break;
 9502        case command_FieldId_trace: { // bool: no argument required but value may be specified as blocking:Y
 9503            *out_anon = false;
 9504        } break;
 9505        case command_FieldId_timeout: { // bool: no argument required but value may be specified as blocking:Y
 9506            *out_anon = false;
 9507        } break;
 9508        case command_FieldId_recvdelay_ns: { // bool: no argument required but value may be specified as blocking:Y
 9509            *out_anon = false;
 9510        } break;
 9511        case command_FieldId_senddelay_ns: { // bool: no argument required but value may be specified as blocking:Y
 9512            *out_anon = false;
 9513        } break;
 9514        case command_FieldId_msgsize_min: { // bool: no argument required but value may be specified as blocking:Y
 9515            *out_anon = false;
 9516        } break;
 9517        case command_FieldId_msgsize_max: { // bool: no argument required but value may be specified as blocking:Y
 9518            *out_anon = false;
 9519        } break;
 9520        case command_FieldId_bufsize: { // bool: no argument required but value may be specified as blocking:Y
 9521            *out_anon = false;
 9522        } break;
 9523        case command_FieldId_recvdelay: { // bool: no argument required but value may be specified as blocking:Y
 9524            *out_anon = false;
 9525        } break;
 9526        default:
 9527        retval=-1; // unrecognized
 9528    }
 9529    return retval;
 9530}
 9531
 9532// --- command.ams_sendtest_proc.ams_sendtest.Start
 9533// Start subprocess
 9534// If subprocess already running, do nothing. Otherwise, start it
 9535int command::ams_sendtest_Start(command::ams_sendtest_proc& parent) {
 9536    int retval = 0;
 9537    if (parent.pid == 0) {
 9538        verblog(ams_sendtest_ToCmdline(parent)); // maybe print command
 9539#ifdef WIN32
 9540        algo_lib::ResolveExecFname(parent.path);
 9541        tempstr cmdline(ams_sendtest_ToCmdline(parent));
 9542        parent.pid = dospawn(Zeroterm(parent.path),Zeroterm(cmdline),parent.timeout,parent.fstdin,parent.fstdout,parent.fstderr);
 9543#else
 9544        parent.pid = fork();
 9545        if (parent.pid == 0) { // child
 9546            algo_lib::DieWithParent();
 9547            if (parent.timeout > 0) {
 9548                alarm(parent.timeout);
 9549            }
 9550            if (retval==0) retval=algo_lib::ApplyRedirect(parent.fstdin , 0);
 9551            if (retval==0) retval=algo_lib::ApplyRedirect(parent.fstdout, 1);
 9552            if (retval==0) retval=algo_lib::ApplyRedirect(parent.fstderr, 2);
 9553            if (retval==0) retval= ams_sendtest_Execv(parent);
 9554            if (retval != 0) { // if start fails, print error
 9555                int err=errno;
 9556                prerr("command.ams_sendtest_execv"
 9557                <<Keyval("errno",err)
 9558                <<Keyval("errstr",strerror(err))
 9559                <<Keyval("comment","Execv failed"));
 9560            }
 9561            _exit(127); // if failed to start, exit anyway
 9562        } else if (parent.pid == -1) {
 9563            retval = errno; // failed to fork
 9564        }
 9565#endif
 9566    }
 9567    parent.status = parent.pid > 0 ? 0 : -1; // if didn't start, set error status
 9568    return retval;
 9569}
 9570
 9571// --- command.ams_sendtest_proc.ams_sendtest.StartRead
 9572// Start subprocess & Read output
 9573algo::Fildes command::ams_sendtest_StartRead(command::ams_sendtest_proc& parent, algo_lib::FFildes &read) {
 9574    int pipefd[2];
 9575    int rc=pipe(pipefd);
 9576    (void)rc;
 9577    read.fd.value = pipefd[0];
 9578    parent.fstdout  << ">&" << pipefd[1];
 9579    ams_sendtest_Start(parent);
 9580    (void)close(pipefd[1]);
 9581    return read.fd;
 9582}
 9583
 9584// --- command.ams_sendtest_proc.ams_sendtest.Kill
 9585// Kill subprocess and wait
 9586void command::ams_sendtest_Kill(command::ams_sendtest_proc& parent) {
 9587    if (parent.pid != 0) {
 9588        kill(parent.pid,9);
 9589        ams_sendtest_Wait(parent);
 9590    }
 9591}
 9592
 9593// --- command.ams_sendtest_proc.ams_sendtest.Wait
 9594// Wait for subprocess to return
 9595void command::ams_sendtest_Wait(command::ams_sendtest_proc& parent) {
 9596    if (parent.pid > 0) {
 9597        int wait_flags = 0;
 9598        int wait_status = 0;
 9599        int rc = -1;
 9600        do {
 9601            // really wait for subprocess to exit
 9602            rc = waitpid(parent.pid,&wait_status,wait_flags);
 9603        } while (rc==-1 && errno==EINTR);
 9604        if (rc == parent.pid) {
 9605            parent.status = wait_status;
 9606            parent.pid = 0;
 9607        }
 9608    }
 9609}
 9610
 9611// --- command.ams_sendtest_proc.ams_sendtest.Exec
 9612// Start + Wait
 9613// Execute subprocess and return exit code
 9614int command::ams_sendtest_Exec(command::ams_sendtest_proc& parent) {
 9615    ams_sendtest_Start(parent);
 9616    ams_sendtest_Wait(parent);
 9617    return parent.status;
 9618}
 9619
 9620// --- command.ams_sendtest_proc.ams_sendtest.ExecX
 9621// Start + Wait, throw exception on error
 9622// Execute subprocess; throw human-readable exception on error
 9623void command::ams_sendtest_ExecX(command::ams_sendtest_proc& parent) {
 9624    int rc = ams_sendtest_Exec(parent);
 9625    vrfy(rc==0, tempstr() << "algo_lib.exec" << Keyval("cmd",ams_sendtest_ToCmdline(parent))
 9626    << Keyval("comment",algo::DescribeWaitStatus(parent.status)));
 9627}
 9628
 9629// --- command.ams_sendtest_proc.ams_sendtest.Execv
 9630// Call execv()
 9631// Call execv with specified parameters
 9632int command::ams_sendtest_Execv(command::ams_sendtest_proc& parent) {
 9633    int ret = 0;
 9634    algo::StringAry args;
 9635    ams_sendtest_ToArgv(parent, args);
 9636    char **argv = (char**)alloca((ary_N(args)+1)*sizeof(*argv));
 9637    ind_beg(algo::StringAry_ary_curs,arg,args) {
 9638        argv[ind_curs(arg).index] = Zeroterm(arg);
 9639    }ind_end;
 9640    argv[ary_N(args)] = NULL;
 9641    // if parent.path is relative, search for it in PATH
 9642    algo_lib::ResolveExecFname(parent.path);
 9643    ret = execv(Zeroterm(parent.path),argv);
 9644    return ret;
 9645}
 9646
 9647// --- command.ams_sendtest_proc.ams_sendtest.ToCmdline
 9648algo::tempstr command::ams_sendtest_ToCmdline(command::ams_sendtest_proc& parent) {
 9649    algo::tempstr retval;
 9650    retval << parent.path << " ";
 9651    command::ams_sendtest_PrintArgv(parent.cmd,retval);
 9652    if (ch_N(parent.fstdin)) {
 9653        retval << " " << parent.fstdin;
 9654    }
 9655    if (ch_N(parent.fstdout)) {
 9656        retval << " " << parent.fstdout;
 9657    }
 9658    if (ch_N(parent.fstderr)) {
 9659        retval << " 2" << parent.fstderr;
 9660    }
 9661    return retval;
 9662}
 9663
 9664// --- command.ams_sendtest_proc.ams_sendtest.ToArgv
 9665// Form array from the command line
 9666void command::ams_sendtest_ToArgv(command::ams_sendtest_proc& parent, algo::StringAry& args) {
 9667    ary_RemoveAll(args);
 9668    ary_Alloc(args) << parent.path;
 9669
 9670    if (parent.cmd.in != "data") {
 9671        cstring *arg = &ary_Alloc(args);
 9672        *arg << "-in:";
 9673        cstring_Print(parent.cmd.in, *arg);
 9674    }
 9675
 9676    if (parent.cmd.id != 0) {
 9677        cstring *arg = &ary_Alloc(args);
 9678        *arg << "-id:";
 9679        i32_Print(parent.cmd.id, *arg);
 9680    }
 9681
 9682    if (parent.cmd.file_prefix != "") {
 9683        cstring *arg = &ary_Alloc(args);
 9684        *arg << "-file_prefix:";
 9685        cstring_Print(parent.cmd.file_prefix, *arg);
 9686    }
 9687
 9688    if (parent.cmd.nchild != 1) {
 9689        cstring *arg = &ary_Alloc(args);
 9690        *arg << "-nchild:";
 9691        i32_Print(parent.cmd.nchild, *arg);
 9692    }
 9693
 9694    if (parent.cmd.blocking != false) {
 9695        cstring *arg = &ary_Alloc(args);
 9696        *arg << "-blocking:";
 9697        bool_Print(parent.cmd.blocking, *arg);
 9698    }
 9699
 9700    if (parent.cmd.nmsg != 1000) {
 9701        cstring *arg = &ary_Alloc(args);
 9702        *arg << "-nmsg:";
 9703        i32_Print(parent.cmd.nmsg, *arg);
 9704    }
 9705
 9706    if (parent.cmd.trace.expr != "") {
 9707        cstring *arg = &ary_Alloc(args);
 9708        *arg << "-trace:";
 9709        command::trace_Print(parent.cmd, *arg);
 9710    }
 9711
 9712    if (parent.cmd.timeout != 30) {
 9713        cstring *arg = &ary_Alloc(args);
 9714        *arg << "-timeout:";
 9715        i32_Print(parent.cmd.timeout, *arg);
 9716    }
 9717
 9718    if (parent.cmd.recvdelay_ns != 0) {
 9719        cstring *arg = &ary_Alloc(args);
 9720        *arg << "-recvdelay_ns:";
 9721        i64_Print(parent.cmd.recvdelay_ns, *arg);
 9722    }
 9723
 9724    if (parent.cmd.senddelay_ns != 0) {
 9725        cstring *arg = &ary_Alloc(args);
 9726        *arg << "-senddelay_ns:";
 9727        i64_Print(parent.cmd.senddelay_ns, *arg);
 9728    }
 9729
 9730    if (parent.cmd.msgsize_min != 64) {
 9731        cstring *arg = &ary_Alloc(args);
 9732        *arg << "-msgsize_min:";
 9733        i32_Print(parent.cmd.msgsize_min, *arg);
 9734    }
 9735
 9736    if (parent.cmd.msgsize_max != 1024) {
 9737        cstring *arg = &ary_Alloc(args);
 9738        *arg << "-msgsize_max:";
 9739        i32_Print(parent.cmd.msgsize_max, *arg);
 9740    }
 9741
 9742    if (parent.cmd.bufsize != 32768) {
 9743        cstring *arg = &ary_Alloc(args);
 9744        *arg << "-bufsize:";
 9745        i32_Print(parent.cmd.bufsize, *arg);
 9746    }
 9747
 9748    if (parent.cmd.recvdelay != 0) {
 9749        cstring *arg = &ary_Alloc(args);
 9750        *arg << "-recvdelay:";
 9751        i64_Print(parent.cmd.recvdelay, *arg);
 9752    }
 9753    for (int i=1; i < algo_lib::_db.cmdline.verbose; ++i) {
 9754        ary_Alloc(args) << "-verbose";
 9755    }
 9756}
 9757
 9758// --- command.ams_sendtest_proc..Uninit
 9759void command::ams_sendtest_proc_Uninit(command::ams_sendtest_proc& parent) {
 9760    command::ams_sendtest_proc &row = parent; (void)row;
 9761
 9762    // command.ams_sendtest_proc.ams_sendtest.Uninit (Exec)  //
 9763    ams_sendtest_Kill(parent); // kill child, ensure forward progress
 9764}
 9765
 9766// --- command.apm.package.Print
 9767// Print back to string
 9768void command::package_Print(command::apm& parent, algo::cstring &out) {
 9769    Regx_Print(parent.package, out);
 9770}
 9771
 9772// --- command.apm.package.ReadStrptrMaybe
 9773// Read Regx from string
 9774// Convert string to field. Return success value
 9775bool command::package_ReadStrptrMaybe(command::apm& parent, algo::strptr in) {
 9776    bool retval = true;
 9777    Regx_ReadSql(parent.package, in, true);
 9778    return retval;
 9779}
 9780
 9781// --- command.apm.ns.Print
 9782// Print back to string
 9783void command::ns_Print(command::apm& parent, algo::cstring &out) {
 9784    Regx_Print(parent.ns, out);
 9785}
 9786
 9787// --- command.apm.ns.ReadStrptrMaybe
 9788// Read Regx from string
 9789// Convert string to field. Return success value
 9790bool command::ns_ReadStrptrMaybe(command::apm& parent, algo::strptr in) {
 9791    bool retval = true;
 9792    Regx_ReadSql(parent.ns, in, true);
 9793    return retval;
 9794}
 9795
 9796// --- command.apm..ReadFieldMaybe
 9797bool command::apm_ReadFieldMaybe(command::apm& parent, algo::strptr field, algo::strptr strval) {
 9798    bool retval = true;
 9799    command::FieldId field_id;
 9800    (void)value_SetStrptrMaybe(field_id,field);
 9801    switch(field_id) {
 9802        case command_FieldId_in: {
 9803            retval = algo::cstring_ReadStrptrMaybe(parent.in, strval);
 9804            break;
 9805        }
 9806        case command_FieldId_pkgdata: {
 9807            retval = algo::cstring_ReadStrptrMaybe(parent.pkgdata, strval);
 9808            break;
 9809        }
 9810        case command_FieldId_package: {
 9811            retval = package_ReadStrptrMaybe(parent, strval);
 9812            break;
 9813        }
 9814        case command_FieldId_ns: {
 9815            retval = ns_ReadStrptrMaybe(parent, strval);
 9816            break;
 9817        }
 9818        case command_FieldId_install: {
 9819            retval = bool_ReadStrptrMaybe(parent.install, strval);
 9820            break;
 9821        }
 9822        case command_FieldId_update: {
 9823            retval = bool_ReadStrptrMaybe(parent.update, strval);
 9824            break;
 9825        }
 9826        case command_FieldId_list: {
 9827            retval = bool_ReadStrptrMaybe(parent.list, strval);
 9828            break;
 9829        }
 9830        case command_FieldId_diff: {
 9831            retval = bool_ReadStrptrMaybe(parent.diff, strval);
 9832            break;
 9833        }
 9834        case command_FieldId_push: {
 9835            retval = bool_ReadStrptrMaybe(parent.push, strval);
 9836            break;
 9837        }
 9838        case command_FieldId_check: {
 9839            retval = bool_ReadStrptrMaybe(parent.check, strval);
 9840            break;
 9841        }
 9842        case command_FieldId_remove: {
 9843            retval = bool_ReadStrptrMaybe(parent.remove, strval);
 9844            break;
 9845        }
 9846        case command_FieldId_origin: {
 9847            retval = algo::Smallstr200_ReadStrptrMaybe(parent.origin, strval);
 9848            break;
 9849        }
 9850        case command_FieldId_ref: {
 9851            retval = algo::Smallstr50_ReadStrptrMaybe(parent.ref, strval);
 9852            break;
 9853        }
 9854        case command_FieldId_dry_run: {
 9855            retval = bool_ReadStrptrMaybe(parent.dry_run, strval);
 9856            break;
 9857        }
 9858        case command_FieldId_showrec: {
 9859            retval = bool_ReadStrptrMaybe(parent.showrec, strval);
 9860            break;
 9861        }
 9862        case command_FieldId_showfile: {
 9863            retval = bool_ReadStrptrMaybe(parent.showfile, strval);
 9864            break;
 9865        }
 9866        case command_FieldId_R: {
 9867            retval = bool_ReadStrptrMaybe(parent.R, strval);
 9868            break;
 9869        }
 9870        case command_FieldId_l: {
 9871            retval = bool_ReadStrptrMaybe(parent.l, strval);
 9872            break;
 9873        }
 9874        case command_FieldId_reset: {
 9875            retval = bool_ReadStrptrMaybe(parent.reset, strval);
 9876            break;
 9877        }
 9878        case command_FieldId_checkclean: {
 9879            retval = bool_ReadStrptrMaybe(parent.checkclean, strval);
 9880            break;
 9881        }
 9882        case command_FieldId_t: {
 9883            retval = bool_ReadStrptrMaybe(parent.t, strval);
 9884            break;
 9885        }
 9886        case command_FieldId_stat: {
 9887            retval = bool_ReadStrptrMaybe(parent.stat, strval);
 9888            break;
 9889        }
 9890        case command_FieldId_annotate: {
 9891            retval = algo::cstring_ReadStrptrMaybe(parent.annotate, strval);
 9892            break;
 9893        }
 9894        case command_FieldId_data_in: {
 9895            retval = algo::cstring_ReadStrptrMaybe(parent.data_in, strval);
 9896            break;
 9897        }
 9898        case command_FieldId_e: {
 9899            retval = bool_ReadStrptrMaybe(parent.e, strval);
 9900            break;
 9901        }
 9902        case command_FieldId_binpath: {
 9903            retval = algo::cstring_ReadStrptrMaybe(parent.binpath, strval);
 9904            break;
 9905        }
 9906        default: break;
 9907    }
 9908    if (!retval) {
 9909        algo_lib::AppendErrtext("attr",field);
 9910    }
 9911    return retval;
 9912}
 9913
 9914// --- command.apm..ReadTupleMaybe
 9915// Read fields of command::apm from attributes of ascii tuple TUPLE
 9916bool command::apm_ReadTupleMaybe(command::apm &parent, algo::Tuple &tuple) {
 9917    bool retval = true;
 9918    int anon_idx = 0;
 9919    ind_beg(algo::Tuple_attrs_curs,attr,tuple) {
 9920        if (ch_N(attr.name) == 0) {
 9921            attr.name = apm_GetAnon(parent, anon_idx++);
 9922        }
 9923        retval = apm_ReadFieldMaybe(parent, attr.name, attr.value);
 9924        if (!retval) {
 9925            break;
 9926        }
 9927    }ind_end;
 9928    return retval;
 9929}
 9930
 9931// --- command.apm..Init
 9932// Set all fields to initial values.
 9933void command::apm_Init(command::apm& parent) {
 9934    parent.in = algo::strptr("data");
 9935    parent.pkgdata = algo::strptr("");
 9936    Regx_ReadSql(parent.package, "", true);
 9937    Regx_ReadSql(parent.ns, "", true);
 9938    parent.install = bool(false);
 9939    parent.update = bool(false);
 9940    parent.list = bool(false);
 9941    parent.diff = bool(false);
 9942    parent.push = bool(false);
 9943    parent.check = bool(false);
 9944    parent.remove = bool(false);
 9945    parent.origin = algo::strptr("");
 9946    parent.ref = algo::strptr("");
 9947    parent.dry_run = bool(false);
 9948    parent.showrec = bool(false);
 9949    parent.showfile = bool(false);
 9950    parent.R = bool(false);
 9951    parent.l = bool(false);
 9952    parent.reset = bool(false);
 9953    parent.checkclean = bool(true);
 9954    parent.t = bool(false);
 9955    parent.stat = bool(false);
 9956    parent.annotate = algo::strptr("");
 9957    parent.data_in = algo::strptr("data");
 9958    parent.e = bool(false);
 9959    parent.binpath = algo::strptr("bin");
 9960}
 9961
 9962// --- command.apm..ToCmdline
 9963// Convenience function that returns a full command line
 9964// Assume command is in a directory called bin
 9965tempstr command::apm_ToCmdline(command::apm& row) {
 9966    tempstr ret;
 9967    ret << "bin/apm ";
 9968    apm_PrintArgv(row, ret);
 9969    // inherit less intense verbose, debug options
 9970    for (int i = 1; i < algo_lib::_db.cmdline.verbose; i++) {
 9971        ret << " -verbose";
 9972    }
 9973    for (int i = 1; i < algo_lib::_db.cmdline.debug; i++) {
 9974        ret << " -debug";
 9975    }
 9976    return ret;
 9977}
 9978
 9979// --- command.apm..PrintArgv
 9980// print string representation of ROW to string STR
 9981// cfmt:command.apm.Argv  printfmt:Tuple
 9982void command::apm_PrintArgv(command::apm& row, algo::cstring& str) {
 9983    algo::tempstr temp;
 9984    (void)temp;
 9985    (void)str;
 9986    if (!(row.in == "data")) {
 9987        ch_RemoveAll(temp);
 9988        cstring_Print(row.in, temp);
 9989        str << " -in:";
 9990        strptr_PrintBash(temp,str);
 9991    }
 9992    if (!(row.pkgdata == "")) {
 9993        ch_RemoveAll(temp);
 9994        cstring_Print(row.pkgdata, temp);
 9995        str << " -pkgdata:";
 9996        strptr_PrintBash(temp,str);
 9997    }
 9998    ch_RemoveAll(temp);
 9999    command::package_Print(const_cast<command::apm&>(row), temp);
10000    str << " -package:";
10001    strptr_PrintBash(temp,str);
10002    if (!(row.ns.expr == "")) {
10003        ch_RemoveAll(temp);
10004        command::ns_Print(const_cast<command::apm&>(row), temp);
10005        str << " -ns:";
10006        strptr_PrintBash(temp,str);
10007    }
10008    if (!(row.install == false)) {
10009        ch_RemoveAll(temp);
10010        bool_Print(row.install, temp);
10011        str << " -install:";
10012        strptr_PrintBash(temp,str);
10013    }
10014    if (!(row.update == false)) {
10015        ch_RemoveAll(temp);
10016        bool_Print(row.update, temp);
10017        str << " -update:";
10018        strptr_PrintBash(temp,str);
10019    }
10020    if (!(row.list == false)) {
10021        ch_RemoveAll(temp);
10022        bool_Print(row.list, temp);
10023        str << " -list:";
10024        strptr_PrintBash(temp,str);
10025    }
10026    if (!(row.diff == false)) {
10027        ch_RemoveAll(temp);
10028        bool_Print(row.diff, temp);
10029        str << " -diff:";
10030        strptr_PrintBash(temp,str);
10031    }
10032    if (!(row.push == false)) {
10033        ch_RemoveAll(temp);
10034        bool_Print(row.push, temp);
10035        str << " -push:";
10036        strptr_PrintBash(temp,str);
10037    }
10038    if (!(row.check == false)) {
10039        ch_RemoveAll(temp);
10040        bool_Print(row.check, temp);
10041        str << " -check:";
10042        strptr_PrintBash(temp,str);
10043    }
10044    if (!(row.remove == false)) {
10045        ch_RemoveAll(temp);
10046        bool_Print(row.remove, temp);
10047        str << " -remove:";
10048        strptr_PrintBash(temp,str);
10049    }
10050    if (!(row.origin == "")) {
10051        ch_RemoveAll(temp);
10052        Smallstr200_Print(row.origin, temp);
10053        str << " -origin:";
10054        strptr_PrintBash(temp,str);
10055    }
10056    if (!(row.ref == "")) {
10057        ch_RemoveAll(temp);
10058        Smallstr50_Print(row.ref, temp);
10059        str << " -ref:";
10060        strptr_PrintBash(temp,str);
10061    }
10062    if (!(row.dry_run == false)) {
10063        ch_RemoveAll(temp);
10064        bool_Print(row.dry_run, temp);
10065        str << " -dry_run:";
10066        strptr_PrintBash(temp,str);
10067    }
10068    if (!(row.showrec == false)) {
10069        ch_RemoveAll(temp);
10070        bool_Print(row.showrec, temp);
10071        str << " -showrec:";
10072        strptr_PrintBash(temp,str);
10073    }
10074    if (!(row.showfile == false)) {
10075        ch_RemoveAll(temp);
10076        bool_Print(row.showfile, temp);
10077        str << " -showfile:";
10078        strptr_PrintBash(temp,str);
10079    }
10080    if (!(row.R == false)) {
10081        ch_RemoveAll(temp);
10082        bool_Print(row.R, temp);
10083        str << " -R:";
10084        strptr_PrintBash(temp,str);
10085    }
10086    if (!(row.l == false)) {
10087        ch_RemoveAll(temp);
10088        bool_Print(row.l, temp);
10089        str << " -l:";
10090        strptr_PrintBash(temp,str);
10091    }
10092    if (!(row.reset == false)) {
10093        ch_RemoveAll(temp);
10094        bool_Print(row.reset, temp);
10095        str << " -reset:";
10096        strptr_PrintBash(temp,str);
10097    }
10098    if (!(row.checkclean == true)) {
10099        ch_RemoveAll(temp);
10100        bool_Print(row.checkclean, temp);
10101        str << " -checkclean:";
10102        strptr_PrintBash(temp,str);
10103    }
10104    if (!(row.t == false)) {
10105        ch_RemoveAll(temp);
10106        bool_Print(row.t, temp);
10107        str << " -t:";
10108        strptr_PrintBash(temp,str);
10109    }
10110    if (!(row.stat == false)) {
10111        ch_RemoveAll(temp);
10112        bool_Print(row.stat, temp);
10113        str << " -stat:";
10114        strptr_PrintBash(temp,str);
10115    }
10116    if (!(row.annotate == "")) {
10117        ch_RemoveAll(temp);
10118        cstring_Print(row.annotate, temp);
10119        str << " -annotate:";
10120        strptr_PrintBash(temp,str);
10121    }
10122    if (!(row.data_in == "data")) {
10123        ch_RemoveAll(temp);
10124        cstring_Print(row.data_in, temp);
10125        str << " -data_in:";
10126        strptr_PrintBash(temp,str);
10127    }
10128    if (!(row.e == false)) {
10129        ch_RemoveAll(temp);
10130        bool_Print(row.e, temp);
10131        str << " -e:";
10132        strptr_PrintBash(temp,str);
10133    }
10134    if (!(row.binpath == "bin")) {
10135        ch_RemoveAll(temp);
10136        cstring_Print(row.binpath, temp);
10137        str << " -binpath:";
10138        strptr_PrintBash(temp,str);
10139    }
10140}
10141
10142// --- command.apm..GetAnon
10143algo::strptr command::apm_GetAnon(command::apm &parent, i32 idx) {
10144    (void)parent;//only to avoid -Wunused-parameter
10145    switch(idx) {
10146        case(0): return strptr("package", 7);
10147        default: return algo::strptr();
10148    }
10149}
10150
10151// --- command.apm..NArgs
10152// Used with command lines
10153// Return # of command-line arguments that must follow this argument
10154// If FIELD is invalid, return -1
10155i32 command::apm_NArgs(command::FieldId field, algo::strptr& out_dflt, bool* out_anon) {
10156    i32 retval = 1;
10157    switch (field) {
10158        case command_FieldId_in: { // $comment
10159            *out_anon = false;
10160        } break;
10161        case command_FieldId_pkgdata: { // $comment
10162            *out_anon = false;
10163        } break;
10164        case command_FieldId_package: { // $comment
10165            *out_anon = true;
10166        } break;
10167        case command_FieldId_ns: { // $comment
10168            *out_anon = false;
10169        } break;
10170        case command_FieldId_install: { // $comment
10171            *out_anon = false;
10172            retval=0;
10173            out_dflt="Y";
10174        } break;
10175        case command_FieldId_update: { // bool: no argument required but value may be specified as install:Y
10176            *out_anon = false;
10177            retval=0;
10178            out_dflt="Y";
10179        } break;
10180        case command_FieldId_list: { // bool: no argument required but value may be specified as update:Y
10181            *out_anon = false;
10182            retval=0;
10183            out_dflt="Y";
10184        } break;
10185        case command_FieldId_diff: { // bool: no argument required but value may be specified as list:Y
10186            *out_anon = false;
10187            retval=0;
10188            out_dflt="Y";
10189        } break;
10190        case command_FieldId_push: { // bool: no argument required but value may be specified as diff:Y
10191            *out_anon = false;
10192            retval=0;
10193            out_dflt="Y";
10194        } break;
10195        case command_FieldId_check: { // bool: no argument required but value may be specified as push:Y
10196            *out_anon = false;
10197            retval=0;
10198            out_dflt="Y";
10199        } break;
10200        case command_FieldId_remove: { // bool: no argument required but value may be specified as check:Y
10201            *out_anon = false;
10202            retval=0;
10203            out_dflt="Y";
10204        } break;
10205        case command_FieldId_origin: { // bool: no argument required but value may be specified as remove:Y
10206            *out_anon = false;
10207        } break;
10208        case command_FieldId_ref: { // bool: no argument required but value may be specified as remove:Y
10209            *out_anon = false;
10210        } break;
10211        case command_FieldId_dry_run: { // bool: no argument required but value may be specified as remove:Y
10212            *out_anon = false;
10213            retval=0;
10214            out_dflt="Y";
10215        } break;
10216        case command_FieldId_showrec: { // bool: no argument required but value may be specified as dry_run:Y
10217            *out_anon = false;
10218            retval=0;
10219            out_dflt="Y";
10220        } break;
10221        case command_FieldId_showfile: { // bool: no argument required but value may be specified as showrec:Y
10222            *out_anon = false;
10223            retval=0;
10224            out_dflt="Y";
10225        } break;
10226        case command_FieldId_R: { // bool: no argument required but value may be specified as showfile:Y
10227            *out_anon = false;
10228            retval=0;
10229            out_dflt="Y";
10230        } break;
10231        case command_FieldId_l: { // bool: no argument required but value may be specified as R:Y
10232            *out_anon = false;
10233            retval=0;
10234            out_dflt="Y";
10235        } break;
10236        case command_FieldId_reset: { // bool: no argument required but value may be specified as l:Y
10237            *out_anon = false;
10238            retval=0;
10239            out_dflt="Y";
10240        } break;
10241        case command_FieldId_checkclean: { // bool: no argument required but value may be specified as reset:Y
10242            *out_anon = false;
10243            retval=0;
10244            out_dflt="Y";
10245        } break;
10246        case command_FieldId_t: { // bool: no argument required but value may be specified as checkclean:Y
10247            *out_anon = false;
10248            retval=0;
10249            out_dflt="Y";
10250        } break;
10251        case command_FieldId_stat: { // bool: no argument required but value may be specified as t:Y
10252            *out_anon = false;
10253            retval=0;
10254            out_dflt="Y";
10255        } break;
10256        case command_FieldId_annotate: { // bool: no argument required but value may be specified as stat:Y
10257            *out_anon = false;
10258        } break;
10259        case command_FieldId_data_in: { // bool: no argument required but value may be specified as stat:Y
10260            *out_anon = false;
10261        } break;
10262        case command_FieldId_e: { // bool: no argument required but value may be specified as stat:Y
10263            *out_anon = false;
10264            retval=0;
10265            out_dflt="Y";
10266        } break;
10267        case command_FieldId_binpath: { // bool: no argument required but value may be specified as e:Y
10268            *out_anon = false;
10269        } break;
10270        default:
10271        retval=-1; // unrecognized
10272    }
10273    return retval;
10274}
10275
10276// --- command.apm_proc.apm.Start
10277// Start subprocess
10278// If subprocess already running, do nothing. Otherwise, start it
10279int command::apm_Start(command::apm_proc& parent) {
10280    int retval = 0;
10281    if (parent.pid == 0) {
10282        verblog(apm_ToCmdline(parent)); // maybe print command
10283#ifdef WIN32
10284        algo_lib::ResolveExecFname(parent.path);
10285        tempstr cmdline(apm_ToCmdline(parent));
10286        parent.pid = dospawn(Zeroterm(parent.path),Zeroterm(cmdline),parent.timeout,parent.fstdin,parent.fstdout,parent.fstderr);
10287#else
10288        parent.pid = fork();
10289        if (parent.pid == 0) { // child
10290            algo_lib::DieWithParent();
10291            if (parent.timeout > 0) {
10292                alarm(parent.timeout);
10293            }
10294            if (retval==0) retval=algo_lib::ApplyRedirect(parent.fstdin , 0);
10295            if (retval==0) retval=algo_lib::ApplyRedirect(parent.fstdout, 1);
10296            if (retval==0) retval=algo_lib::ApplyRedirect(parent.fstderr, 2);
10297            if (retval==0) retval= apm_Execv(parent);
10298            if (retval != 0) { // if start fails, print error
10299                int err=errno;
10300                prerr("command.apm_execv"
10301                <<Keyval("errno",err)
10302                <<Keyval("errstr",strerror(err))
10303                <<Keyval("comment","Execv failed"));
10304            }
10305            _exit(127); // if failed to start, exit anyway
10306        } else if (parent.pid == -1) {
10307            retval = errno; // failed to fork
10308        }
10309#endif
10310    }
10311    parent.status = parent.pid > 0 ? 0 : -1; // if didn't start, set error status
10312    return retval;
10313}
10314
10315// --- command.apm_proc.apm.StartRead
10316// Start subprocess & Read output
10317algo::Fildes command::apm_StartRead(command::apm_proc& parent, algo_lib::FFildes &read) {
10318    int pipefd[2];
10319    int rc=pipe(pipefd);
10320    (void)rc;
10321    read.fd.value = pipefd[0];
10322    parent.fstdout  << ">&" << pipefd[1];
10323    apm_Start(parent);
10324    (void)close(pipefd[1]);
10325    return read.fd;
10326}
10327
10328// --- command.apm_proc.apm.Kill
10329// Kill subprocess and wait
10330void command::apm_Kill(command::apm_proc& parent) {
10331    if (parent.pid != 0) {
10332        kill(parent.pid,9);
10333        apm_Wait(parent);
10334    }
10335}
10336
10337// --- command.apm_proc.apm.Wait
10338// Wait for subprocess to return
10339void command::apm_Wait(command::apm_proc& parent) {
10340    if (parent.pid > 0) {
10341        int wait_flags = 0;
10342        int wait_status = 0;
10343        int rc = -1;
10344        do {
10345            // really wait for subprocess to exit
10346            rc = waitpid(parent.pid,&wait_status,wait_flags);
10347        } while (rc==-1 && errno==EINTR);
10348        if (rc == parent.pid) {
10349            parent.status = wait_status;
10350            parent.pid = 0;
10351        }
10352    }
10353}
10354
10355// --- command.apm_proc.apm.Exec
10356// Start + Wait
10357// Execute subprocess and return exit code
10358int command::apm_Exec(command::apm_proc& parent) {
10359    apm_Start(parent);
10360    apm_Wait(parent);
10361    return parent.status;
10362}
10363
10364// --- command.apm_proc.apm.ExecX
10365// Start + Wait, throw exception on error
10366// Execute subprocess; throw human-readable exception on error
10367void command::apm_ExecX(command::apm_proc& parent) {
10368    int rc = apm_Exec(parent);
10369    vrfy(rc==0, tempstr() << "algo_lib.exec" << Keyval("cmd",apm_ToCmdline(parent))
10370    << Keyval("comment",algo::DescribeWaitStatus(parent.status)));
10371}
10372
10373// --- command.apm_proc.apm.Execv
10374// Call execv()
10375// Call execv with specified parameters
10376int command::apm_Execv(command::apm_proc& parent) {
10377    int ret = 0;
10378    algo::StringAry args;
10379    apm_ToArgv(parent, args);
10380    char **argv = (char**)alloca((ary_N(args)+1)*sizeof(*argv));
10381    ind_beg(algo::StringAry_ary_curs,arg,args) {
10382        argv[ind_curs(arg).index] = Zeroterm(arg);
10383    }ind_end;
10384    argv[ary_N(args)] = NULL;
10385    // if parent.path is relative, search for it in PATH
10386    algo_lib::ResolveExecFname(parent.path);
10387    ret = execv(Zeroterm(parent.path),argv);
10388    return ret;
10389}
10390
10391// --- command.apm_proc.apm.ToCmdline
10392algo::tempstr command::apm_ToCmdline(command::apm_proc& parent) {
10393    algo::tempstr retval;
10394    retval << parent.path << " ";
10395    command::apm_PrintArgv(parent.cmd,retval);
10396    if (ch_N(parent.fstdin)) {
10397        retval << " " << parent.fstdin;
10398    }
10399    if (ch_N(parent.fstdout)) {
10400        retval << " " << parent.fstdout;
10401    }
10402    if (ch_N(parent.fstderr)) {
10403        retval << " 2" << parent.fstderr;
10404    }
10405    return retval;
10406}
10407
10408// --- command.apm_proc.apm.ToArgv
10409// Form array from the command line
10410void command::apm_ToArgv(command::apm_proc& parent, algo::StringAry& args) {
10411    ary_RemoveAll(args);
10412    ary_Alloc(args) << parent.path;
10413
10414    if (parent.cmd.in != "data") {
10415        cstring *arg = &ary_Alloc(args);
10416        *arg << "-in:";
10417        cstring_Print(parent.cmd.in, *arg);
10418    }
10419
10420    if (parent.cmd.pkgdata != "") {
10421        cstring *arg = &ary_Alloc(args);
10422        *arg << "-pkgdata:";
10423        cstring_Print(parent.cmd.pkgdata, *arg);
10424    }
10425
10426    if (parent.cmd.package.expr != "") {
10427        cstring *arg = &ary_Alloc(args);
10428        *arg << "-package:";
10429        command::package_Print(parent.cmd, *arg);
10430    }
10431
10432    if (parent.cmd.ns.expr != "") {
10433        cstring *arg = &ary_Alloc(args);
10434        *arg << "-ns:";
10435        command::ns_Print(parent.cmd, *arg);
10436    }
10437
10438    if (parent.cmd.install != false) {
10439        cstring *arg = &ary_Alloc(args);
10440        *arg << "-install:";
10441        bool_Print(parent.cmd.install, *arg);
10442    }
10443
10444    if (parent.cmd.update != false) {
10445        cstring *arg = &ary_Alloc(args);
10446        *arg << "-update:";
10447        bool_Print(parent.cmd.update, *arg);
10448    }
10449
10450    if (parent.cmd.list != false) {
10451        cstring *arg = &ary_Alloc(args);
10452        *arg << "-list:";
10453        bool_Print(parent.cmd.list, *arg);
10454    }
10455
10456    if (parent.cmd.diff != false) {
10457        cstring *arg = &ary_Alloc(args);
10458        *arg << "-diff:";
10459        bool_Print(parent.cmd.diff, *arg);
10460    }
10461
10462    if (parent.cmd.push != false) {
10463        cstring *arg = &ary_Alloc(args);
10464        *arg << "-push:";
10465        bool_Print(parent.cmd.push, *arg);
10466    }
10467
10468    if (parent.cmd.check != false) {
10469        cstring *arg = &ary_Alloc(args);
10470        *arg << "-check:";
10471        bool_Print(parent.cmd.check, *arg);
10472    }
10473
10474    if (parent.cmd.remove != false) {
10475        cstring *arg = &ary_Alloc(args);
10476        *arg << "-remove:";
10477        bool_Print(parent.cmd.remove, *arg);
10478    }
10479
10480    if (parent.cmd.origin != "") {
10481        cstring *arg = &ary_Alloc(args);
10482        *arg << "-origin:";
10483        Smallstr200_Print(parent.cmd.origin, *arg);
10484    }
10485
10486    if (parent.cmd.ref != "") {
10487        cstring *arg = &ary_Alloc(args);
10488        *arg << "-ref:";
10489        Smallstr50_Print(parent.cmd.ref, *arg);
10490    }
10491
10492    if (parent.cmd.dry_run != false) {
10493        cstring *arg = &ary_Alloc(args);
10494        *arg << "-dry_run:";
10495        bool_Print(parent.cmd.dry_run, *arg);
10496    }
10497
10498    if (parent.cmd.showrec != false) {
10499        cstring *arg = &ary_Alloc(args);
10500        *arg << "-showrec:";
10501        bool_Print(parent.cmd.showrec, *arg);
10502    }
10503
10504    if (parent.cmd.showfile != false) {
10505        cstring *arg = &ary_Alloc(args);
10506        *arg << "-showfile:";
10507        bool_Print(parent.cmd.showfile, *arg);
10508    }
10509
10510    if (parent.cmd.R != false) {
10511        cstring *arg = &ary_Alloc(args);
10512        *arg << "-R:";
10513        bool_Print(parent.cmd.R, *arg);
10514    }
10515
10516    if (parent.cmd.l != false) {
10517        cstring *arg = &ary_Alloc(args);
10518        *arg << "-l:";
10519        bool_Print(parent.cmd.l, *arg);
10520    }
10521
10522    if (parent.cmd.reset != false) {
10523        cstring *arg = &ary_Alloc(args);
10524        *arg << "-reset:";
10525        bool_Print(parent.cmd.reset, *arg);
10526    }
10527
10528    if (parent.cmd.checkclean != true) {
10529        cstring *arg = &ary_Alloc(args);
10530        *arg << "-checkclean:";
10531        bool_Print(parent.cmd.checkclean, *arg);
10532    }
10533
10534    if (parent.cmd.t != false) {
10535        cstring *arg = &ary_Alloc(args);
10536        *arg << "-t:";
10537        bool_Print(parent.cmd.t, *arg);
10538    }
10539
10540    if (parent.cmd.stat != false) {
10541        cstring *arg = &ary_Alloc(args);
10542        *arg << "-stat:";
10543        bool_Print(parent.cmd.stat, *arg);
10544    }
10545
10546    if (parent.cmd.annotate != "") {
10547        cstring *arg = &ary_Alloc(args);
10548        *arg << "-annotate:";
10549        cstring_Print(parent.cmd.annotate, *arg);
10550    }
10551
10552    if (parent.cmd.data_in != "data") {
10553        cstring *arg = &ary_Alloc(args);
10554        *arg << "-data_in:";
10555        cstring_Print(parent.cmd.data_in, *arg);
10556    }
10557
10558    if (parent.cmd.e != false) {
10559        cstring *arg = &ary_Alloc(args);
10560        *arg << "-e:";
10561        bool_Print(parent.cmd.e, *arg);
10562    }
10563
10564    if (parent.cmd.binpath != "bin") {
10565        cstring *arg = &ary_Alloc(args);
10566        *arg << "-binpath:";
10567        cstring_Print(parent.cmd.binpath, *arg);
10568    }
10569    for (int i=1; i < algo_lib::_db.cmdline.verbose; ++i) {
10570        ary_Alloc(args) << "-verbose";
10571    }
10572}
10573
10574// --- command.apm_proc..Uninit
10575void command::apm_proc_Uninit(command::apm_proc& parent) {
10576    command::apm_proc &row = parent; (void)row;
10577
10578    // command.apm_proc.apm.Uninit (Exec)  //
10579    apm_Kill(parent); // kill child, ensure forward progress
10580}
10581
10582// --- command.atf_amc.amctest.Print
10583// Print back to string
10584void command::amctest_Print(command::atf_amc& parent, algo::cstring &out) {
10585    Regx_Print(parent.amctest, out);
10586}
10587
10588// --- command.atf_amc.amctest.ReadStrptrMaybe
10589// Read Regx from string
10590// Convert string to field. Return success value
10591bool command::amctest_ReadStrptrMaybe(command::atf_amc& parent, algo::strptr in) {
10592    bool retval = true;
10593    Regx_ReadSql(parent.amctest, in, true);
10594    return retval;
10595}
10596
10597// --- command.atf_amc..ReadFieldMaybe
10598bool command::atf_amc_ReadFieldMaybe(command::atf_amc& parent, algo::strptr field, algo::strptr strval) {
10599    bool retval = true;
10600    command::FieldId field_id;
10601    (void)value_SetStrptrMaybe(field_id,field);
10602    switch(field_id) {
10603        case command_FieldId_in: {
10604            retval = algo::cstring_ReadStrptrMaybe(parent.in, strval);
10605            break;
10606        }
10607        case command_FieldId_amctest: {
10608            retval = amctest_ReadStrptrMaybe(parent, strval);
10609            break;
10610        }
10611        case command_FieldId_dofork: {
10612            retval = bool_ReadStrptrMaybe(parent.dofork, strval);
10613            break;
10614        }
10615        case command_FieldId_q: {
10616            retval = bool_ReadStrptrMaybe(parent.q, strval);
10617            break;
10618        }
10619        default: break;
10620    }
10621    if (!retval) {
10622        algo_lib::AppendErrtext("attr",field);
10623    }
10624    return retval;
10625}
10626
10627// --- command.atf_amc..ReadTupleMaybe
10628// Read fields of command::atf_amc from attributes of ascii tuple TUPLE
10629bool command::atf_amc_ReadTupleMaybe(command::atf_amc &parent, algo::Tuple &tuple) {
10630    bool retval = true;
10631    int anon_idx = 0;
10632    ind_beg(algo::Tuple_attrs_curs,attr,tuple) {
10633        if (ch_N(attr.name) == 0) {
10634            attr.name = atf_amc_GetAnon(parent, anon_idx++);
10635        }
10636        retval = atf_amc_ReadFieldMaybe(parent, attr.name, attr.value);
10637        if (!retval) {
10638            break;
10639        }
10640    }ind_end;
10641    return retval;
10642}
10643
10644// --- command.atf_amc..Init
10645// Set all fields to initial values.
10646void command::atf_amc_Init(command::atf_amc& parent) {
10647    parent.in = algo::strptr("data");
10648    Regx_ReadSql(parent.amctest, "%", true);
10649    parent.dofork = bool(true);
10650    parent.q = bool(false);
10651}
10652
10653// --- command.atf_amc..ToCmdline
10654// Convenience function that returns a full command line
10655// Assume command is in a directory called bin
10656tempstr command::atf_amc_ToCmdline(command::atf_amc& row) {
10657    tempstr ret;
10658    ret << "bin/atf_amc ";
10659    atf_amc_PrintArgv(row, ret);
10660    // inherit less intense verbose, debug options
10661    for (int i = 1; i < algo_lib::_db.cmdline.verbose; i++) {
10662        ret << " -verbose";
10663    }
10664    for (int i = 1; i < algo_lib::_db.cmdline.debug; i++) {
10665        ret << " -debug";
10666    }
10667    return ret;
10668}
10669
10670// --- command.atf_amc..PrintArgv
10671// print string representation of ROW to string STR
10672// cfmt:command.atf_amc.Argv  printfmt:Tuple
10673void command::atf_amc_PrintArgv(command::atf_amc& row, algo::cstring& str) {
10674    algo::tempstr temp;
10675    (void)temp;
10676    (void)str;
10677    if (!(row.in == "data")) {
10678        ch_RemoveAll(temp);
10679        cstring_Print(row.in, temp);
10680        str << " -in:";
10681        strptr_PrintBash(temp,str);
10682    }
10683    ch_RemoveAll(temp);
10684    command::amctest_Print(const_cast<command::atf_amc&>(row), temp);
10685    str << " -amctest:";
10686    strptr_PrintBash(temp,str);
10687    if (!(row.dofork == true)) {
10688        ch_RemoveAll(temp);
10689        bool_Print(row.dofork, temp);
10690        str << " -dofork:";
10691        strptr_PrintBash(temp,str);
10692    }
10693    if (!(row.q == false)) {
10694        ch_RemoveAll(temp);
10695        bool_Print(row.q, temp);
10696        str << " -q:";
10697        strptr_PrintBash(temp,str);
10698    }
10699}
10700
10701// --- command.atf_amc..GetAnon
10702algo::strptr command::atf_amc_GetAnon(command::atf_amc &parent, i32 idx) {
10703    (void)parent;//only to avoid -Wunused-parameter
10704    switch(idx) {
10705        case(0): return strptr("amctest", 7);
10706        default: return algo::strptr();
10707    }
10708}
10709
10710// --- command.atf_amc..NArgs
10711// Used with command lines
10712// Return # of command-line arguments that must follow this argument
10713// If FIELD is invalid, return -1
10714i32 command::atf_amc_NArgs(command::FieldId field, algo::strptr& out_dflt, bool* out_anon) {
10715    i32 retval = 1;
10716    switch (field) {
10717        case command_FieldId_in: { // $comment
10718            *out_anon = false;
10719        } break;
10720        case command_FieldId_amctest: { // $comment
10721            *out_anon = true;
10722        } break;
10723        case command_FieldId_dofork: { // $comment
10724            *out_anon = false;
10725            retval=0;
10726            out_dflt="Y";
10727        } break;
10728        case command_FieldId_q: { // bool: no argument required but value may be specified as dofork:Y
10729            *out_anon = false;
10730            retval=0;
10731            out_dflt="Y";
10732        } break;
10733        default:
10734        retval=-1; // unrecognized
10735    }
10736    return retval;
10737}
10738
10739// --- command.atf_amc_proc.atf_amc.Start
10740// Start subprocess
10741// If subprocess already running, do nothing. Otherwise, start it
10742int command::atf_amc_Start(command::atf_amc_proc& parent) {
10743    int retval = 0;
10744    if (parent.pid == 0) {
10745        verblog(atf_amc_ToCmdline(parent)); // maybe print command
10746#ifdef WIN32
10747        algo_lib::ResolveExecFname(parent.path);
10748        tempstr cmdline(atf_amc_ToCmdline(parent));
10749        parent.pid = dospawn(Zeroterm(parent.path),Zeroterm(cmdline),parent.timeout,parent.fstdin,parent.fstdout,parent.fstderr);
10750#else
10751        parent.pid = fork();
10752        if (parent.pid == 0) { // child
10753            algo_lib::DieWithParent();
10754            if (parent.timeout > 0) {
10755                alarm(parent.timeout);
10756            }
10757            if (retval==0) retval=algo_lib::ApplyRedirect(parent.fstdin , 0);
10758            if (retval==0) retval=algo_lib::ApplyRedirect(parent.fstdout, 1);
10759            if (retval==0) retval=algo_lib::ApplyRedirect(parent.fstderr, 2);
10760            if (retval==0) retval= atf_amc_Execv(parent);
10761            if (retval != 0) { // if start fails, print error
10762                int err=errno;
10763                prerr("command.atf_amc_execv"
10764                <<Keyval("errno",err)
10765                <<Keyval("errstr",strerror(err))
10766                <<Keyval("comment","Execv failed"));
10767            }
10768            _exit(127); // if failed to start, exit anyway
10769        } else if (parent.pid == -1) {
10770            retval = errno; // failed to fork
10771        }
10772#endif
10773    }
10774    parent.status = parent.pid > 0 ? 0 : -1; // if didn't start, set error status
10775    return retval;
10776}
10777
10778// --- command.atf_amc_proc.atf_amc.StartRead
10779// Start subprocess & Read output
10780algo::Fildes command::atf_amc_StartRead(command::atf_amc_proc& parent, algo_lib::FFildes &read) {
10781    int pipefd[2];
10782    int rc=pipe(pipefd);
10783    (void)rc;
10784    read.fd.value = pipefd[0];
10785    parent.fstdout  << ">&" << pipefd[1];
10786    atf_amc_Start(parent);
10787    (void)close(pipefd[1]);
10788    return read.fd;
10789}
10790
10791// --- command.atf_amc_proc.atf_amc.Kill
10792// Kill subprocess and wait
10793void command::atf_amc_Kill(command::atf_amc_proc& parent) {
10794    if (parent.pid != 0) {
10795        kill(parent.pid,9);
10796        atf_amc_Wait(parent);
10797    }
10798}
10799
10800// --- command.atf_amc_proc.atf_amc.Wait
10801// Wait for subprocess to return
10802void command::atf_amc_Wait(command::atf_amc_proc& parent) {
10803    if (parent.pid > 0) {
10804        int wait_flags = 0;
10805        int wait_status = 0;
10806        int rc = -1;
10807        do {
10808            // really wait for subprocess to exit
10809            rc = waitpid(parent.pid,&wait_status,wait_flags);
10810        } while (rc==-1 && errno==EINTR);
10811        if (rc == parent.pid) {
10812            parent.status = wait_status;
10813            parent.pid = 0;
10814        }
10815    }
10816}
10817
10818// --- command.atf_amc_proc.atf_amc.Exec
10819// Start + Wait
10820// Execute subprocess and return exit code
10821int command::atf_amc_Exec(command::atf_amc_proc& parent) {
10822    atf_amc_Start(parent);
10823    atf_amc_Wait(parent);
10824    return parent.status;
10825}
10826
10827// --- command.atf_amc_proc.atf_amc.ExecX
10828// Start + Wait, throw exception on error
10829// Execute subprocess; throw human-readable exception on error
10830void command::atf_amc_ExecX(command::atf_amc_proc& parent) {
10831    int rc = atf_amc_Exec(parent);
10832    vrfy(rc==0, tempstr() << "algo_lib.exec" << Keyval("cmd",atf_amc_ToCmdline(parent))
10833    << Keyval("comment",algo::DescribeWaitStatus(parent.status)));
10834}
10835
10836// --- command.atf_amc_proc.atf_amc.Execv
10837// Call execv()
10838// Call execv with specified parameters
10839int command::atf_amc_Execv(command::atf_amc_proc& parent) {
10840    int ret = 0;
10841    algo::StringAry args;
10842    atf_amc_ToArgv(parent, args);
10843    char **argv = (char**)alloca((ary_N(args)+1)*sizeof(*argv));
10844    ind_beg(algo::StringAry_ary_curs,arg,args) {
10845        argv[ind_curs(arg).index] = Zeroterm(arg);
10846    }ind_end;
10847    argv[ary_N(args)] = NULL;
10848    // if parent.path is relative, search for it in PATH
10849    algo_lib::ResolveExecFname(parent.path);
10850    ret = execv(Zeroterm(parent.path),argv);
10851    return ret;
10852}
10853
10854// --- command.atf_amc_proc.atf_amc.ToCmdline
10855algo::tempstr command::atf_amc_ToCmdline(command::atf_amc_proc& parent) {
10856    algo::tempstr retval;
10857    retval << parent.path << " ";
10858    command::atf_amc_PrintArgv(parent.cmd,retval);
10859    if (ch_N(parent.fstdin)) {
10860        retval << " " << parent.fstdin;
10861    }
10862    if (ch_N(parent.fstdout)) {
10863        retval << " " << parent.fstdout;
10864    }
10865    if (ch_N(parent.fstderr)) {
10866        retval << " 2" << parent.fstderr;
10867    }
10868    return retval;
10869}
10870
10871// --- command.atf_amc_proc.atf_amc.ToArgv
10872// Form array from the command line
10873void command::atf_amc_ToArgv(command::atf_amc_proc& parent, algo::StringAry& args) {
10874    ary_RemoveAll(args);
10875    ary_Alloc(args) << parent.path;
10876
10877    if (parent.cmd.in != "data") {
10878        cstring *arg = &ary_Alloc(args);
10879        *arg << "-in:";
10880        cstring_Print(parent.cmd.in, *arg);
10881    }
10882
10883    if (parent.cmd.amctest.expr != "%") {
10884        cstring *arg = &ary_Alloc(args);
10885        *arg << "-amctest:";
10886        command::amctest_Print(parent.cmd, *arg);
10887    }
10888
10889    if (parent.cmd.dofork != true) {
10890        cstring *arg = &ary_Alloc(args);
10891        *arg << "-dofork:";
10892        bool_Print(parent.cmd.dofork, *arg);
10893    }
10894
10895    if (parent.cmd.q != false) {
10896        cstring *arg = &ary_Alloc(args);
10897        *arg << "-q:";
10898        bool_Print(parent.cmd.q, *arg);
10899    }
10900    for (int i=1; i < algo_lib::_db.cmdline.verbose; ++i) {
10901        ary_Alloc(args) << "-verbose";
10902    }
10903}
10904
10905// --- command.atf_amc_proc..Uninit
10906void command::atf_amc_proc_Uninit(command::atf_amc_proc& parent) {
10907    command::atf_amc_proc &row = parent; (void)row;
10908
10909    // command.atf_amc_proc.atf_amc.Uninit (Exec)  //
10910    atf_amc_Kill(parent); // kill child, ensure forward progress
10911}
10912
10913// --- command.atf_ci.citest.Print
10914// Print back to string
10915void command::citest_Print(command::atf_ci& parent, algo::cstring &out) {
10916    Regx_Print(parent.citest, out);
10917}
10918
10919// --- command.atf_ci.citest.ReadStrptrMaybe
10920// Read Regx from string
10921// Convert string to field. Return success value
10922bool command::citest_ReadStrptrMaybe(command::atf_ci& parent, algo::strptr in) {
10923    bool retval = true;
10924    Regx_ReadSql(parent.citest, in, true);
10925    return retval;
10926}
10927
10928// --- command.atf_ci.cijob.Print
10929// Print back to string
10930void command::cijob_Print(command::atf_ci& parent, algo::cstring &out) {
10931    Regx_Print(parent.cijob, out);
10932}
10933
10934// --- command.atf_ci.cijob.ReadStrptrMaybe
10935// Read Regx from string
10936// Convert string to field. Return success value
10937bool command::cijob_ReadStrptrMaybe(command::atf_ci& parent, algo::strptr in) {
10938    bool retval = true;
10939    Regx_ReadSql(parent.cijob, in, true);
10940    return retval;
10941}
10942
10943// --- command.atf_ci..ReadFieldMaybe
10944bool command::atf_ci_ReadFieldMaybe(command::atf_ci& parent, algo::strptr field, algo::strptr strval) {
10945    bool retval = true;
10946    command::FieldId field_id;
10947    (void)value_SetStrptrMaybe(field_id,field);
10948    switch(field_id) {
10949        case command_FieldId_in: {
10950            retval = algo::cstring_ReadStrptrMaybe(parent.in, strval);
10951            break;
10952        }
10953        case command_FieldId_citest: {
10954            retval = citest_ReadStrptrMaybe(parent, strval);
10955            break;
10956        }
10957        case command_FieldId_maxerr: {
10958            retval = i32_ReadStrptrMaybe(parent.maxerr, strval);
10959            break;
10960        }
10961        case command_FieldId_cijob: {
10962            retval = cijob_ReadStrptrMaybe(parent, strval);
10963            break;
10964        }
10965        case command_FieldId_capture: {
10966            retval = bool_ReadStrptrMaybe(parent.capture, strval);
10967            break;
10968        }
10969        default: break;
10970    }
10971    if (!retval) {
10972        algo_lib::AppendErrtext("attr",field);
10973    }
10974    return retval;
10975}
10976
10977// --- command.atf_ci..ReadTupleMaybe
10978// Read fields of command::atf_ci from attributes of ascii tuple TUPLE
10979bool command::atf_ci_ReadTupleMaybe(command::atf_ci &parent, algo::Tuple &tuple) {
10980    bool retval = true;
10981    int anon_idx = 0;
10982    ind_beg(algo::Tuple_attrs_curs,attr,tuple) {
10983        if (ch_N(attr.name) == 0) {
10984            attr.name = atf_ci_GetAnon(parent, anon_idx++);
10985        }
10986        retval = atf_ci_ReadFieldMaybe(parent, attr.name, attr.value);
10987        if (!retval) {
10988            break;
10989        }
10990    }ind_end;
10991    return retval;
10992}
10993
10994// --- command.atf_ci..Init
10995// Set all fields to initial values.
10996void command::atf_ci_Init(command::atf_ci& parent) {
10997    parent.in = algo::strptr("data");
10998    Regx_ReadSql(parent.citest, "%", true);
10999    parent.maxerr = i32(0);
11000    Regx_ReadSql(parent.cijob, "%", true);
11001    parent.capture = bool(false);
11002}
11003
11004// --- command.atf_ci..ToCmdline
11005// Convenience function that returns a full command line
11006// Assume command is in a directory called bin
11007tempstr command::atf_ci_ToCmdline(command::atf_ci& row) {
11008    tempstr ret;
11009    ret << "bin/atf_ci ";
11010    atf_ci_PrintArgv(row, ret);
11011    // inherit less intense verbose, debug options
11012    for (int i = 1; i < algo_lib::_db.cmdline.verbose; i++) {
11013        ret << " -verbose";
11014    }
11015    for (int i = 1; i < algo_lib::_db.cmdline.debug; i++) {
11016        ret << " -debug";
11017    }
11018    return ret;
11019}
11020
11021// --- command.atf_ci..PrintArgv
11022// print string representation of ROW to string STR
11023// cfmt:command.atf_ci.Argv  printfmt:Tuple
11024void command::atf_ci_PrintArgv(command::atf_ci& row, algo::cstring& str) {
11025    algo::tempstr temp;
11026    (void)temp;
11027    (void)str;
11028    if (!(row.in == "data")) {
11029        ch_RemoveAll(temp);
11030        cstring_Print(row.in, temp);
11031        str << " -in:";
11032        strptr_PrintBash(temp,str);
11033    }
11034    ch_RemoveAll(temp);
11035    command::citest_Print(const_cast<command::atf_ci&>(row), temp);
11036    str << " -citest:";
11037    strptr_PrintBash(temp,str);
11038    if (!(row.maxerr == 0)) {
11039        ch_RemoveAll(temp);
11040        i32_Print(row.maxerr, temp);
11041        str << " -maxerr:";
11042        strptr_PrintBash(temp,str);
11043    }
11044    if (!(row.cijob.expr == "%")) {
11045        ch_RemoveAll(temp);
11046        command::cijob_Print(const_cast<command::atf_ci&>(row), temp);
11047        str << " -cijob:";
11048        strptr_PrintBash(temp,str);
11049    }
11050    if (!(row.capture == false)) {
11051        ch_RemoveAll(temp);
11052        bool_Print(row.capture, temp);
11053        str << " -capture:";
11054        strptr_PrintBash(temp,str);
11055    }
11056}
11057
11058// --- command.atf_ci..GetAnon
11059algo::strptr command::atf_ci_GetAnon(command::atf_ci &parent, i32 idx) {
11060    (void)parent;//only to avoid -Wunused-parameter
11061    switch(idx) {
11062        case(0): return strptr("citest", 6);
11063        default: return algo::strptr();
11064    }
11065}
11066
11067// --- command.atf_ci..NArgs
11068// Used with command lines
11069// Return # of command-line arguments that must follow this argument
11070// If FIELD is invalid, return -1
11071i32 command::atf_ci_NArgs(command::FieldId field, algo::strptr& out_dflt, bool* out_anon) {
11072    i32 retval = 1;
11073    switch (field) {
11074        case command_FieldId_in: { // $comment
11075            *out_anon = false;
11076        } break;
11077        case command_FieldId_citest: { // $comment
11078            *out_anon = true;
11079        } break;
11080        case command_FieldId_maxerr: { // $comment
11081            *out_anon = false;
11082        } break;
11083        case command_FieldId_cijob: { // $comment
11084            *out_anon = false;
11085        } break;
11086        case command_FieldId_capture: { // $comment
11087            *out_anon = false;
11088            retval=0;
11089            out_dflt="Y";
11090        } break;
11091        default:
11092        retval=-1; // unrecognized
11093    }
11094    return retval;
11095}
11096
11097// --- command.atf_ci_proc.atf_ci.Start
11098// Start subprocess
11099// If subprocess already running, do nothing. Otherwise, start it
11100int command::atf_ci_Start(command::atf_ci_proc& parent) {
11101    int retval = 0;
11102    if (parent.pid == 0) {
11103        verblog(atf_ci_ToCmdline(parent)); // maybe print command
11104#ifdef WIN32
11105        algo_lib::ResolveExecFname(parent.path);
11106        tempstr cmdline(atf_ci_ToCmdline(parent));
11107        parent.pid = dospawn(Zeroterm(parent.path),Zeroterm(cmdline),parent.timeout,parent.fstdin,parent.fstdout,parent.fstderr);
11108#else
11109        parent.pid = fork();
11110        if (parent.pid == 0) { // child
11111            algo_lib::DieWithParent();
11112            if (parent.timeout > 0) {
11113                alarm(parent.timeout);
11114            }
11115            if (retval==0) retval=algo_lib::ApplyRedirect(parent.fstdin , 0);
11116            if (retval==0) retval=algo_lib::ApplyRedirect(parent.fstdout, 1);
11117            if (retval==0) retval=algo_lib::ApplyRedirect(parent.fstderr, 2);
11118            if (retval==0) retval= atf_ci_Execv(parent);
11119            if (retval != 0) { // if start fails, print error
11120                int err=errno;
11121                prerr("command.atf_ci_execv"
11122                <<Keyval("errno",err)
11123                <<Keyval("errstr",strerror(err))
11124                <<Keyval("comment","Execv failed"));
11125            }
11126            _exit(127); // if failed to start, exit anyway
11127        } else if (parent.pid == -1) {
11128            retval = errno; // failed to fork
11129        }
11130#endif
11131    }
11132    parent.status = parent.pid > 0 ? 0 : -1; // if didn't start, set error status
11133    return retval;
11134}
11135
11136// --- command.atf_ci_proc.atf_ci.StartRead
11137// Start subprocess & Read output
11138algo::Fildes command::atf_ci_StartRead(command::atf_ci_proc& parent, algo_lib::FFildes &read) {
11139    int pipefd[2];
11140    int rc=pipe(pipefd);
11141    (void)rc;
11142    read.fd.value = pipefd[0];
11143    parent.fstdout  << ">&" << pipefd[1];
11144    atf_ci_Start(parent);
11145    (void)close(pipefd[1]);
11146    return read.fd;
11147}
11148
11149// --- command.atf_ci_proc.atf_ci.Kill
11150// Kill subprocess and wait
11151void command::atf_ci_Kill(command::atf_ci_proc& parent) {
11152    if (parent.pid != 0) {
11153        kill(parent.pid,9);
11154        atf_ci_Wait(parent);
11155    }
11156}
11157
11158// --- command.atf_ci_proc.atf_ci.Wait
11159// Wait for subprocess to return
11160void command::atf_ci_Wait(command::atf_ci_proc& parent) {
11161    if (parent.pid > 0) {
11162        int wait_flags = 0;
11163        int wait_status = 0;
11164        int rc = -1;
11165        do {
11166            // really wait for subprocess to exit
11167            rc = waitpid(parent.pid,&wait_status,wait_flags);
11168        } while (rc==-1 && errno==EINTR);
11169        if (rc == parent.pid) {
11170            parent.status = wait_status;
11171            parent.pid = 0;
11172        }
11173    }
11174}
11175
11176// --- command.atf_ci_proc.atf_ci.Exec
11177// Start + Wait
11178// Execute subprocess and return exit code
11179int command::atf_ci_Exec(command::atf_ci_proc& parent) {
11180    atf_ci_Start(parent);
11181    atf_ci_Wait(parent);
11182    return parent.status;
11183}
11184
11185// --- command.atf_ci_proc.atf_ci.ExecX
11186// Start + Wait, throw exception on error
11187// Execute subprocess; throw human-readable exception on error
11188void command::atf_ci_ExecX(command::atf_ci_proc& parent) {
11189    int rc = atf_ci_Exec(parent);
11190    vrfy(rc==0, tempstr() << "algo_lib.exec" << Keyval("cmd",atf_ci_ToCmdline(parent))
11191    << Keyval("comment",algo::DescribeWaitStatus(parent.status)));
11192}
11193
11194// --- command.atf_ci_proc.atf_ci.Execv
11195// Call execv()
11196// Call execv with specified parameters
11197int command::atf_ci_Execv(command::atf_ci_proc& parent) {
11198    int ret = 0;
11199    algo::StringAry args;
11200    atf_ci_ToArgv(parent, args);
11201    char **argv = (char**)alloca((ary_N(args)+1)*sizeof(*argv));
11202    ind_beg(algo::StringAry_ary_curs,arg,args) {
11203        argv[ind_curs(arg).index] = Zeroterm(arg);
11204    }ind_end;
11205    argv[ary_N(args)] = NULL;
11206    // if parent.path is relative, search for it in PATH
11207    algo_lib::ResolveExecFname(parent.path);
11208    ret = execv(Zeroterm(parent.path),argv);
11209    return ret;
11210}
11211
11212// --- command.atf_ci_proc.atf_ci.ToCmdline
11213algo::tempstr command::atf_ci_ToCmdline(command::atf_ci_proc& parent) {
11214    algo::tempstr retval;
11215    retval << parent.path << " ";
11216    command::atf_ci_PrintArgv(parent.cmd,retval);
11217    if (ch_N(parent.fstdin)) {
11218        retval << " " << parent.fstdin;
11219    }
11220    if (ch_N(parent.fstdout)) {
11221        retval << " " << parent.fstdout;
11222    }
11223    if (ch_N(parent.fstderr)) {
11224        retval << " 2" << parent.fstderr;
11225    }
11226    return retval;
11227}
11228
11229// --- command.atf_ci_proc.atf_ci.ToArgv
11230// Form array from the command line
11231void command::atf_ci_ToArgv(command::atf_ci_proc& parent, algo::StringAry& args) {
11232    ary_RemoveAll(args);
11233    ary_Alloc(args) << parent.path;
11234
11235    if (parent.cmd.in != "data") {
11236        cstring *arg = &ary_Alloc(args);
11237        *arg << "-in:";
11238        cstring_Print(parent.cmd.in, *arg);
11239    }
11240
11241    if (parent.cmd.citest.expr != "%") {
11242        cstring *arg = &ary_Alloc(args);
11243        *arg << "-citest:";
11244        command::citest_Print(parent.cmd, *arg);
11245    }
11246
11247    if (parent.cmd.maxerr != 0) {
11248        cstring *arg = &ary_Alloc(args);
11249        *arg << "-maxerr:";
11250        i32_Print(parent.cmd.maxerr, *arg);
11251    }
11252
11253    if (parent.cmd.cijob.expr != "%") {
11254        cstring *arg = &ary_Alloc(args);
11255        *arg << "-cijob:";
11256        command::cijob_Print(parent.cmd, *arg);
11257    }
11258
11259    if (parent.cmd.capture != false) {
11260        cstring *arg = &ary_Alloc(args);
11261        *arg << "-capture:";
11262        bool_Print(parent.cmd.capture, *arg);
11263    }
11264    for (int i=1; i < algo_lib::_db.cmdline.verbose; ++i) {
11265        ary_Alloc(args) << "-verbose";
11266    }
11267}
11268
11269// --- command.atf_ci_proc..Uninit
11270void command::atf_ci_proc_Uninit(command::atf_ci_proc& parent) {
11271    command::atf_ci_proc &row = parent; (void)row;
11272
11273    // command.atf_ci_proc.atf_ci.Uninit (Exec)  //
11274    atf_ci_Kill(parent); // kill child, ensure forward progress
11275}
11276
11277// --- command.atf_cmdline.mstr.Addary
11278// Reserve space (this may move memory). Insert N element at the end.
11279// Return aryptr to newly inserted block.
11280// If the RHS argument aliases the array (refers to the same memory), exit program with fatal error.
11281algo::aryptr<algo::cstring> command::mstr_Addary(command::atf_cmdline& parent, algo::aryptr<algo::cstring> rhs) {
11282    bool overlaps = rhs.n_elems>0 && rhs.elems >= parent.mstr_elems && rhs.elems < parent.mstr_elems + parent.mstr_max;
11283    if (UNLIKELY(overlaps)) {
11284        FatalErrorExit("command.tary_alias  field:command.atf_cmdline.mstr  comment:'alias error: sub-array is being appended to the whole'");
11285    }
11286    int nnew = rhs.n_elems;
11287    mstr_Reserve(parent, nnew); // reserve space
11288    int at = parent.mstr_n;
11289    for (int i = 0; i < nnew; i++) {
11290        new (parent.mstr_elems + at + i) algo::cstring(rhs[i]);
11291        parent.mstr_n++;
11292    }
11293    return algo::aryptr<algo::cstring>(parent.mstr_elems + at, nnew);
11294}
11295
11296// --- command.atf_cmdline.mstr.Alloc
11297// Reserve space. Insert element at the end
11298// The new element is initialized to a default value
11299algo::cstring& command::mstr_Alloc(command::atf_cmdline& parent) {
11300    mstr_Reserve(parent, 1);
11301    int n  = parent.mstr_n;
11302    int at = n;
11303    algo::cstring *elems = parent.mstr_elems;
11304    new (elems + at) algo::cstring(); // construct new element, default initializer
11305    parent.mstr_n = n+1;
11306    return elems[at];
11307}
11308
11309// --- command.atf_cmdline.mstr.AllocAt
11310// Reserve space for new element, reallocating the array if necessary
11311// Insert new element at specified index. Index must be in range or a fatal error occurs.
11312algo::cstring& command::mstr_AllocAt(command::atf_cmdline& parent, int at) {
11313    mstr_Reserve(parent, 1);
11314    int n  = parent.mstr_n;
11315    if (UNLIKELY(u64(at) >= u64(n+1))) {
11316        FatalErrorExit("command.bad_alloc_at  field:command.atf_cmdline.mstr  comment:'index out of range'");
11317    }
11318    algo::cstring *elems = parent.mstr_elems;
11319    memmove(elems + at + 1, elems + at, (n - at) * sizeof(algo::cstring));
11320    new (elems + at) algo::cstring(); // construct element, default initializer
11321    parent.mstr_n = n+1;
11322    return elems[at];
11323}
11324
11325// --- command.atf_cmdline.mstr.AllocN
11326// Reserve space. Insert N elements at the end of the array, return pointer to array
11327algo::aryptr<algo::cstring> command::mstr_AllocN(command::atf_cmdline& parent, int n_elems) {
11328    mstr_Reserve(parent, n_elems);
11329    int old_n  = parent.mstr_n;
11330    int new_n = old_n + n_elems;
11331    algo::cstring *elems = parent.mstr_elems;
11332    for (int i = old_n; i < new_n; i++) {
11333        new (elems + i) algo::cstring(); // construct new element, default initialize
11334    }
11335    parent.mstr_n = new_n;
11336    return algo::aryptr<algo::cstring>(elems + old_n, n_elems);
11337}
11338
11339// --- command.atf_cmdline.mstr.Remove
11340// Remove item by index. If index outside of range, do nothing.
11341void command::mstr_Remove(command::atf_cmdline& parent, u32 i) {
11342    u32 lim = parent.mstr_n;
11343    algo::cstring *elems = parent.mstr_elems;
11344    if (i < lim) {
11345        elems[i].~cstring(); // destroy element
11346        memmove(elems + i, elems + (i + 1), sizeof(algo::cstring) * (lim - (i + 1)));
11347        parent.mstr_n = lim - 1;
11348    }
11349}
11350
11351// --- command.atf_cmdline.mstr.RemoveAll
11352void command::mstr_RemoveAll(command::atf_cmdline& parent) {
11353    u32 n = parent.mstr_n;
11354    while (n > 0) {
11355        n -= 1;
11356        parent.mstr_elems[n].~cstring();
11357        parent.mstr_n = n;
11358    }
11359}
11360
11361// --- command.atf_cmdline.mstr.RemoveLast
11362// Delete last element of array. Do nothing if array is empty.
11363void command::mstr_RemoveLast(command::atf_cmdline& parent) {
11364    u64 n = parent.mstr_n;
11365    if (n > 0) {
11366        n -= 1;
11367        mstr_qFind(parent, u64(n)).~cstring();
11368        parent.mstr_n = n;
11369    }
11370}
11371
11372// --- command.atf_cmdline.mstr.AbsReserve
11373// Make sure N elements fit in array. Process dies if out of memory
11374void command::mstr_AbsReserve(command::atf_cmdline& parent, int n) {
11375    u32 old_max  = parent.mstr_max;
11376    if (n > i32(old_max)) {
11377        u32 new_max  = i32_Max(i32_Max(old_max * 2, n), 4);
11378        void *new_mem = algo_lib::malloc_ReallocMem(parent.mstr_elems, old_max * sizeof(algo::cstring), new_max * sizeof(algo::cstring));
11379        if (UNLIKELY(!new_mem)) {
11380            FatalErrorExit("command.tary_nomem  field:command.atf_cmdline.mstr  comment:'out of memory'");
11381        }
11382        parent.mstr_elems = (algo::cstring*)new_mem;
11383        parent.mstr_max = new_max;
11384    }
11385}
11386
11387// --- command.atf_cmdline.mstr.Setary
11388// Copy contents of RHS to PARENT.
11389void command::mstr_Setary(command::atf_cmdline& parent, command::atf_cmdline &rhs) {
11390    mstr_RemoveAll(parent);
11391    int nnew = rhs.mstr_n;
11392    mstr_Reserve(parent, nnew); // reserve space
11393    for (int i = 0; i < nnew; i++) { // copy elements over
11394        new (parent.mstr_elems + i) algo::cstring(mstr_qFind(rhs, i));
11395        parent.mstr_n = i + 1;
11396    }
11397}
11398
11399// --- command.atf_cmdline.mstr.Setary2
11400// Copy specified array into mstr, discarding previous contents.
11401// If the RHS argument aliases the array (refers to the same memory), throw exception.
11402void command::mstr_Setary(command::atf_cmdline& parent, const algo::aryptr<algo::cstring> &rhs) {
11403    mstr_RemoveAll(parent);
11404    mstr_Addary(parent, rhs);
11405}
11406
11407// --- command.atf_cmdline.mstr.AllocNVal
11408// Reserve space. Insert N elements at the end of the array, return pointer to array
11409algo::aryptr<algo::cstring> command::mstr_AllocNVal(command::atf_cmdline& parent, int n_elems, const algo::cstring& val) {
11410    mstr_Reserve(parent, n_elems);
11411    int old_n  = parent.mstr_n;
11412    int new_n = old_n + n_elems;
11413    algo::cstring *elems = parent.mstr_elems;
11414    for (int i = old_n; i < new_n; i++) {
11415        new (elems + i) algo::cstring(val);
11416    }
11417    parent.mstr_n = new_n;
11418    return algo::aryptr<algo::cstring>(elems + old_n, n_elems);
11419}
11420
11421// --- command.atf_cmdline.mstr.ReadStrptrMaybe
11422// A single element is read from input string and appended to the array.
11423// If the string contains an error, the array is untouched.
11424// Function returns success value.
11425bool command::mstr_ReadStrptrMaybe(command::atf_cmdline& parent, algo::strptr in_str) {
11426    bool retval = true;
11427    algo::cstring &elem = mstr_Alloc(parent);
11428    retval = algo::cstring_ReadStrptrMaybe(elem, in_str);
11429    if (!retval) {
11430        mstr_RemoveLast(parent);
11431    }
11432    return retval;
11433}
11434
11435// --- command.atf_cmdline.mnum.Addary
11436// Reserve space (this may move memory). Insert N element at the end.
11437// Return aryptr to newly inserted block.
11438// If the RHS argument aliases the array (refers to the same memory), exit program with fatal error.
11439algo::aryptr<i32> command::mnum_Addary(command::atf_cmdline& parent, algo::aryptr<i32> rhs) {
11440    bool overlaps = rhs.n_elems>0 && rhs.elems >= parent.mnum_elems && rhs.elems < parent.mnum_elems + parent.mnum_max;
11441    if (UNLIKELY(overlaps)) {
11442        FatalErrorExit("command.tary_alias  field:command.atf_cmdline.mnum  comment:'alias error: sub-array is being appended to the whole'");
11443    }
11444    int nnew = rhs.n_elems;
11445    mnum_Reserve(parent, nnew); // reserve space
11446    int at = parent.mnum_n;
11447    memcpy(parent.mnum_elems + at, rhs.elems, nnew * sizeof(i32));
11448    parent.mnum_n += nnew;
11449    return algo::aryptr<i32>(parent.mnum_elems + at, nnew);
11450}
11451
11452// --- command.atf_cmdline.mnum.Alloc
11453// Reserve space. Insert element at the end
11454// The new element is initialized to a default value
11455i32& command::mnum_Alloc(command::atf_cmdline& parent) {
11456    mnum_Reserve(parent, 1);
11457    int n  = parent.mnum_n;
11458    int at = n;
11459    i32 *elems = parent.mnum_elems;
11460    new (elems + at) i32(0); // construct new element, default initializer
11461    parent.mnum_n = n+1;
11462    return elems[at];
11463}
11464
11465// --- command.atf_cmdline.mnum.AllocAt
11466// Reserve space for new element, reallocating the array if necessary
11467// Insert new element at specified index. Index must be in range or a fatal error occurs.
11468i32& command::mnum_AllocAt(command::atf_cmdline& parent, int at) {
11469    mnum_Reserve(parent, 1);
11470    int n  = parent.mnum_n;
11471    if (UNLIKELY(u64(at) >= u64(n+1))) {
11472        FatalErrorExit("command.bad_alloc_at  field:command.atf_cmdline.mnum  comment:'index out of range'");
11473    }
11474    i32 *elems = parent.mnum_elems;
11475    memmove(elems + at + 1, elems + at, (n - at) * sizeof(i32));
11476    new (elems + at) i32(0); // construct element, default initializer
11477    parent.mnum_n = n+1;
11478    return elems[at];
11479}
11480
11481// --- command.atf_cmdline.mnum.AllocN
11482// Reserve space. Insert N elements at the end of the array, return pointer to array
11483algo::aryptr<i32> command::mnum_AllocN(command::atf_cmdline& parent, int n_elems) {
11484    mnum_Reserve(parent, n_elems);
11485    int old_n  = parent.mnum_n;
11486    int new_n = old_n + n_elems;
11487    i32 *elems = parent.mnum_elems;
11488    for (int i = old_n; i < new_n; i++) {
11489        new (elems + i) i32(0); // construct new element, default initialize
11490    }
11491    parent.mnum_n = new_n;
11492    return algo::aryptr<i32>(elems + old_n, n_elems);
11493}
11494
11495// --- command.atf_cmdline.mnum.Remove
11496// Remove item by index. If index outside of range, do nothing.
11497void command::mnum_Remove(command::atf_cmdline& parent, u32 i) {
11498    u32 lim = parent.mnum_n;
11499    i32 *elems = parent.mnum_elems;
11500    if (i < lim) {
11501        memmove(elems + i, elems + (i + 1), sizeof(i32) * (lim - (i + 1)));
11502        parent.mnum_n = lim - 1;
11503    }
11504}
11505
11506// --- command.atf_cmdline.mnum.RemoveLast
11507// Delete last element of array. Do nothing if array is empty.
11508void command::mnum_RemoveLast(command::atf_cmdline& parent) {
11509    u64 n = parent.mnum_n;
11510    if (n > 0) {
11511        n -= 1;
11512        parent.mnum_n = n;
11513    }
11514}
11515
11516// --- command.atf_cmdline.mnum.AbsReserve
11517// Make sure N elements fit in array. Process dies if out of memory
11518void command::mnum_AbsReserve(command::atf_cmdline& parent, int n) {
11519    u32 old_max  = parent.mnum_max;
11520    if (n > i32(old_max)) {
11521        u32 new_max  = i32_Max(i32_Max(old_max * 2, n), 4);
11522        void *new_mem = algo_lib::malloc_ReallocMem(parent.mnum_elems, old_max * sizeof(i32), new_max * sizeof(i32));
11523        if (UNLIKELY(!new_mem)) {
11524            FatalErrorExit("command.tary_nomem  field:command.atf_cmdline.mnum  comment:'out of memory'");
11525        }
11526        parent.mnum_elems = (i32*)new_mem;
11527        parent.mnum_max = new_max;
11528    }
11529}
11530
11531// --- command.atf_cmdline.mnum.Setary
11532// Copy contents of RHS to PARENT.
11533void command::mnum_Setary(command::atf_cmdline& parent, command::atf_cmdline &rhs) {
11534    mnum_RemoveAll(parent);
11535    int nnew = rhs.mnum_n;
11536    mnum_Reserve(parent, nnew); // reserve space
11537    for (int i = 0; i < nnew; i++) { // copy elements over
11538        new (parent.mnum_elems + i) i32(mnum_qFind(rhs, i));
11539        parent.mnum_n = i + 1;
11540    }
11541}
11542
11543// --- command.atf_cmdline.mnum.Setary2
11544// Copy specified array into mnum, discarding previous contents.
11545// If the RHS argument aliases the array (refers to the same memory), throw exception.
11546void command::mnum_Setary(command::atf_cmdline& parent, const algo::aryptr<i32> &rhs) {
11547    mnum_RemoveAll(parent);
11548    mnum_Addary(parent, rhs);
11549}
11550
11551// --- command.atf_cmdline.mnum.AllocNVal
11552// Reserve space. Insert N elements at the end of the array, return pointer to array
11553algo::aryptr<i32> command::mnum_AllocNVal(command::atf_cmdline& parent, int n_elems, const i32& val) {
11554    mnum_Reserve(parent, n_elems);
11555    int old_n  = parent.mnum_n;
11556    int new_n = old_n + n_elems;
11557    i32 *elems = parent.mnum_elems;
11558    for (int i = old_n; i < new_n; i++) {
11559        new (elems + i) i32(val);
11560    }
11561    parent.mnum_n = new_n;
11562    return algo::aryptr<i32>(elems + old_n, n_elems);
11563}
11564
11565// --- command.atf_cmdline.mnum.ReadStrptrMaybe
11566// A single element is read from input string and appended to the array.
11567// If the string contains an error, the array is untouched.
11568// Function returns success value.
11569bool command::mnum_ReadStrptrMaybe(command::atf_cmdline& parent, algo::strptr in_str) {
11570    bool retval = true;
11571    i32 &elem = mnum_Alloc(parent);
11572    retval = i32_ReadStrptrMaybe(elem, in_str);
11573    if (!retval) {
11574        mnum_RemoveLast(parent);
11575    }
11576    return retval;
11577}
11578
11579// --- command.atf_cmdline.mdbl.Addary
11580// Reserve space (this may move memory). Insert N element at the end.
11581// Return aryptr to newly inserted block.
11582// If the RHS argument aliases the array (refers to the same memory), exit program with fatal error.
11583algo::aryptr<double> command::mdbl_Addary(command::atf_cmdline& parent, algo::aryptr<double> rhs) {
11584    bool overlaps = rhs.n_elems>0 && rhs.elems >= parent.mdbl_elems && rhs.elems < parent.mdbl_elems + parent.mdbl_max;
11585    if (UNLIKELY(overlaps)) {
11586        FatalErrorExit("command.tary_alias  field:command.atf_cmdline.mdbl  comment:'alias error: sub-array is being appended to the whole'");
11587    }
11588    int nnew = rhs.n_elems;
11589    mdbl_Reserve(parent, nnew); // reserve space
11590    int at = parent.mdbl_n;
11591    memcpy(parent.mdbl_elems + at, rhs.elems, nnew * sizeof(double));
11592    parent.mdbl_n += nnew;
11593    return algo::aryptr<double>(parent.mdbl_elems + at, nnew);
11594}
11595
11596// --- command.atf_cmdline.mdbl.Alloc
11597// Reserve space. Insert element at the end
11598// The new element is initialized to a default value
11599double& command::mdbl_Alloc(command::atf_cmdline& parent) {
11600    mdbl_Reserve(parent, 1);
11601    int n  = parent.mdbl_n;
11602    int at = n;
11603    double *elems = parent.mdbl_elems;
11604    new (elems + at) double(0.0); // construct new element, default initializer
11605    parent.mdbl_n = n+1;
11606    return elems[at];
11607}
11608
11609// --- command.atf_cmdline.mdbl.AllocAt
11610// Reserve space for new element, reallocating the array if necessary
11611// Insert new element at specified index. Index must be in range or a fatal error occurs.
11612double& command::mdbl_AllocAt(command::atf_cmdline& parent, int at) {
11613    mdbl_Reserve(parent, 1);
11614    int n  = parent.mdbl_n;
11615    if (UNLIKELY(u64(at) >= u64(n+1))) {
11616        FatalErrorExit("command.bad_alloc_at  field:command.atf_cmdline.mdbl  comment:'index out of range'");
11617    }
11618    double *elems = parent.mdbl_elems;
11619    memmove(elems + at + 1, elems + at, (n - at) * sizeof(double));
11620    new (elems + at) double(0.0); // construct element, default initializer
11621    parent.mdbl_n = n+1;
11622    return elems[at];
11623}
11624
11625// --- command.atf_cmdline.mdbl.AllocN
11626// Reserve space. Insert N elements at the end of the array, return pointer to array
11627algo::aryptr<double> command::mdbl_AllocN(command::atf_cmdline& parent, int n_elems) {
11628    mdbl_Reserve(parent, n_elems);
11629    int old_n  = parent.mdbl_n;
11630    int new_n = old_n + n_elems;
11631    double *elems = parent.mdbl_elems;
11632    for (int i = old_n; i < new_n; i++) {
11633        new (elems + i) double(0.0); // construct new element, default initialize
11634    }
11635    parent.mdbl_n = new_n;
11636    return algo::aryptr<double>(elems + old_n, n_elems);
11637}
11638
11639// --- command.atf_cmdline.mdbl.Remove
11640// Remove item by index. If index outside of range, do nothing.
11641void command::mdbl_Remove(command::atf_cmdline& parent, u32 i) {
11642    u32 lim = parent.mdbl_n;
11643    double *elems = parent.mdbl_elems;
11644    if (i < lim) {
11645        memmove(elems + i, elems + (i + 1), sizeof(double) * (lim - (i + 1)));
11646        parent.mdbl_n = lim - 1;
11647    }
11648}
11649
11650// --- command.atf_cmdline.mdbl.RemoveLast
11651// Delete last element of array. Do nothing if array is empty.
11652void command::mdbl_RemoveLast(command::atf_cmdline& parent) {
11653    u64 n = parent.mdbl_n;
11654    if (n > 0) {
11655        n -= 1;
11656        parent.mdbl_n = n;
11657    }
11658}
11659
11660// --- command.atf_cmdline.mdbl.AbsReserve
11661// Make sure N elements fit in array. Process dies if out of memory
11662void command::mdbl_AbsReserve(command::atf_cmdline& parent, int n) {
11663    u32 old_max  = parent.mdbl_max;
11664    if (n > i32(old_max)) {
11665        u32 new_max  = i32_Max(i32_Max(old_max * 2, n), 4);
11666        void *new_mem = algo_lib::malloc_ReallocMem(parent.mdbl_elems, old_max * sizeof(double), new_max * sizeof(double));
11667        if (UNLIKELY(!new_mem)) {
11668            FatalErrorExit("command.tary_nomem  field:command.atf_cmdline.mdbl  comment:'out of memory'");
11669        }
11670        parent.mdbl_elems = (double*)new_mem;
11671        parent.mdbl_max = new_max;
11672    }
11673}
11674
11675// --- command.atf_cmdline.mdbl.Setary
11676// Copy contents of RHS to PARENT.
11677void command::mdbl_Setary(command::atf_cmdline& parent, command::atf_cmdline &rhs) {
11678    mdbl_RemoveAll(parent);
11679    int nnew = rhs.mdbl_n;
11680    mdbl_Reserve(parent, nnew); // reserve space
11681    for (int i = 0; i < nnew; i++) { // copy elements over
11682        new (parent.mdbl_elems + i) double(mdbl_qFind(rhs, i));
11683        parent.mdbl_n = i + 1;
11684    }
11685}
11686
11687// --- command.atf_cmdline.mdbl.Setary2
11688// Copy specified array into mdbl, discarding previous contents.
11689// If the RHS argument aliases the array (refers to the same memory), throw exception.
11690void command::mdbl_Setary(command::atf_cmdline& parent, const algo::aryptr<double> &rhs) {
11691    mdbl_RemoveAll(parent);
11692    mdbl_Addary(parent, rhs);
11693}
11694
11695// --- command.atf_cmdline.mdbl.AllocNVal
11696// Reserve space. Insert N elements at the end of the array, return pointer to array
11697algo::aryptr<double> command::mdbl_AllocNVal(command::atf_cmdline& parent, int n_elems, const double& val) {
11698    mdbl_Reserve(parent, n_elems);
11699    int old_n  = parent.mdbl_n;
11700    int new_n = old_n + n_elems;
11701    double *elems = parent.mdbl_elems;
11702    for (int i = old_n; i < new_n; i++) {
11703        new (elems + i) double(val);
11704    }
11705    parent.mdbl_n = new_n;
11706    return algo::aryptr<double>(elems + old_n, n_elems);
11707}
11708
11709// --- command.atf_cmdline.mdbl.ReadStrptrMaybe
11710// A single element is read from input string and appended to the array.
11711// If the string contains an error, the array is untouched.
11712// Function returns success value.
11713bool command::mdbl_ReadStrptrMaybe(command::atf_cmdline& parent, algo::strptr in_str) {
11714    bool retval = true;
11715    double &elem = mdbl_Alloc(parent);
11716    retval = double_ReadStrptrMaybe(elem, in_str);
11717    if (!retval) {
11718        mdbl_RemoveLast(parent);
11719    }
11720    return retval;
11721}
11722
11723// --- command.atf_cmdline.amnum.Addary
11724// Reserve space (this may move memory). Insert N element at the end.
11725// Return aryptr to newly inserted block.
11726// If the RHS argument aliases the array (refers to the same memory), exit program with fatal error.
11727algo::aryptr<i32> command::amnum_Addary(command::atf_cmdline& parent, algo::aryptr<i32> rhs) {
11728    bool overlaps = rhs.n_elems>0 && rhs.elems >= parent.amnum_elems && rhs.elems < parent.amnum_elems + parent.amnum_max;
11729    if (UNLIKELY(overlaps)) {
11730        FatalErrorExit("command.tary_alias  field:command.atf_cmdline.amnum  comment:'alias error: sub-array is being appended to the whole'");
11731    }
11732    int nnew = rhs.n_elems;
11733    amnum_Reserve(parent, nnew); // reserve space
11734    int at = parent.amnum_n;
11735    memcpy(parent.amnum_elems + at, rhs.elems, nnew * sizeof(i32));
11736    parent.amnum_n += nnew;
11737    return algo::aryptr<i32>(parent.amnum_elems + at, nnew);
11738}
11739
11740// --- command.atf_cmdline.amnum.Alloc
11741// Reserve space. Insert element at the end
11742// The new element is initialized to a default value
11743i32& command::amnum_Alloc(command::atf_cmdline& parent) {
11744    amnum_Reserve(parent, 1);
11745    int n  = parent.amnum_n;
11746    int at = n;
11747    i32 *elems = parent.amnum_elems;
11748    new (elems + at) i32(0); // construct new element, default initializer
11749    parent.amnum_n = n+1;
11750    return elems[at];
11751}
11752
11753// --- command.atf_cmdline.amnum.AllocAt
11754// Reserve space for new element, reallocating the array if necessary
11755// Insert new element at specified index. Index must be in range or a fatal error occurs.
11756i32& command::amnum_AllocAt(command::atf_cmdline& parent, int at) {
11757    amnum_Reserve(parent, 1);
11758    int n  = parent.amnum_n;
11759    if (UNLIKELY(u64(at) >= u64(n+1))) {
11760        FatalErrorExit("command.bad_alloc_at  field:command.atf_cmdline.amnum  comment:'index out of range'");
11761    }
11762    i32 *elems = parent.amnum_elems;
11763    memmove(elems + at + 1, elems + at, (n - at) * sizeof(i32));
11764    new (elems + at) i32(0); // construct element, default initializer
11765    parent.amnum_n = n+1;
11766    return elems[at];
11767}
11768
11769// --- command.atf_cmdline.amnum.AllocN
11770// Reserve space. Insert N elements at the end of the array, return pointer to array
11771algo::aryptr<i32> command::amnum_AllocN(command::atf_cmdline& parent, int n_elems) {
11772    amnum_Reserve(parent, n_elems);
11773    int old_n  = parent.amnum_n;
11774    int new_n = old_n + n_elems;
11775    i32 *elems = parent.amnum_elems;
11776    for (int i = old_n; i < new_n; i++) {
11777        new (elems + i) i32(0); // construct new element, default initialize
11778    }
11779    parent.amnum_n = new_n;
11780    return algo::aryptr<i32>(elems + old_n, n_elems);
11781}
11782
11783// --- command.atf_cmdline.amnum.Remove
11784// Remove item by index. If index outside of range, do nothing.
11785void command::amnum_Remove(command::atf_cmdline& parent, u32 i) {
11786    u32 lim = parent.amnum_n;
11787    i32 *elems = parent.amnum_elems;
11788    if (i < lim) {
11789        memmove(elems + i, elems + (i + 1), sizeof(i32) * (lim - (i + 1)));
11790        parent.amnum_n = lim - 1;
11791    }
11792}
11793
11794// --- command.atf_cmdline.amnum.RemoveLast
11795// Delete last element of array. Do nothing if array is empty.
11796void command::amnum_RemoveLast(command::atf_cmdline& parent) {
11797    u64 n = parent.amnum_n;
11798    if (n > 0) {
11799        n -= 1;
11800        parent.amnum_n = n;
11801    }
11802}
11803
11804// --- command.atf_cmdline.amnum.AbsReserve
11805// Make sure N elements fit in array. Process dies if out of memory
11806void command::amnum_AbsReserve(command::atf_cmdline& parent, int n) {
11807    u32 old_max  = parent.amnum_max;
11808    if (n > i32(old_max)) {
11809        u32 new_max  = i32_Max(i32_Max(old_max * 2, n), 4);
11810        void *new_mem = algo_lib::malloc_ReallocMem(parent.amnum_elems, old_max * sizeof(i32), new_max * sizeof(i32));
11811        if (UNLIKELY(!new_mem)) {
11812            FatalErrorExit("command.tary_nomem  field:command.atf_cmdline.amnum  comment:'out of memory'");
11813        }
11814        parent.amnum_elems = (i32*)new_mem;
11815        parent.amnum_max = new_max;
11816    }
11817}
11818
11819// --- command.atf_cmdline.amnum.Setary
11820// Copy contents of RHS to PARENT.
11821void command::amnum_Setary(command::atf_cmdline& parent, command::atf_cmdline &rhs) {
11822    amnum_RemoveAll(parent);
11823    int nnew = rhs.amnum_n;
11824    amnum_Reserve(parent, nnew); // reserve space
11825    for (int i = 0; i < nnew; i++) { // copy elements over
11826        new (parent.amnum_elems + i) i32(amnum_qFind(rhs, i));
11827        parent.amnum_n = i + 1;
11828    }
11829}
11830
11831// --- command.atf_cmdline.amnum.Setary2
11832// Copy specified array into amnum, discarding previous contents.
11833// If the RHS argument aliases the array (refers to the same memory), throw exception.
11834void command::amnum_Setary(command::atf_cmdline& parent, const algo::aryptr<i32> &rhs) {
11835    amnum_RemoveAll(parent);
11836    amnum_Addary(parent, rhs);
11837}
11838
11839// --- command.atf_cmdline.amnum.AllocNVal
11840// Reserve space. Insert N elements at the end of the array, return pointer to array
11841algo::aryptr<i32> command::amnum_AllocNVal(command::atf_cmdline& parent, int n_elems, const i32& val) {
11842    amnum_Reserve(parent, n_elems);
11843    int old_n  = parent.amnum_n;
11844    int new_n = old_n + n_elems;
11845    i32 *elems = parent.amnum_elems;
11846    for (int i = old_n; i < new_n; i++) {
11847        new (elems + i) i32(val);
11848    }
11849    parent.amnum_n = new_n;
11850    return algo::aryptr<i32>(elems + old_n, n_elems);
11851}
11852
11853// --- command.atf_cmdline.amnum.ReadStrptrMaybe
11854// A single element is read from input string and appended to the array.
11855// If the string contains an error, the array is untouched.
11856// Function returns success value.
11857bool command::amnum_ReadStrptrMaybe(command::atf_cmdline& parent, algo::strptr in_str) {
11858    bool retval = true;
11859    i32 &elem = amnum_Alloc(parent);
11860    retval = i32_ReadStrptrMaybe(elem, in_str);
11861    if (!retval) {
11862        amnum_RemoveLast(parent);
11863    }
11864    return retval;
11865}
11866
11867// --- command.atf_cmdline.fconst.ToCstr
11868// Convert numeric value of field to one of predefined string constants.
11869// If string is found, return a static C string. Otherwise, return NULL.
11870const char* command::fconst_ToCstr(const command::atf_cmdline& parent) {
11871    const char *ret = NULL;
11872    switch(fconst_GetEnum(parent)) {
11873        case command_atf_cmdline_fconst_high: ret = "high";  break;
11874        case command_atf_cmdline_fconst_medium: ret = "medium";  break;
11875        case command_atf_cmdline_fconst_low: ret = "low";  break;
11876    }
11877    return ret;
11878}
11879
11880// --- command.atf_cmdline.fconst.Print
11881// Convert fconst to a string. First, attempt conversion to a known string.
11882// If no string matches, print fconst as a numeric value.
11883void command::fconst_Print(const command::atf_cmdline& parent, algo::cstring &lhs) {
11884    const char *strval = fconst_ToCstr(parent);
11885    if (strval) {
11886        lhs << strval;
11887    } else {
11888        lhs << parent.fconst;
11889    }
11890}
11891
11892// --- command.atf_cmdline.fconst.SetStrptrMaybe
11893// Convert string to field.
11894// If the string is invalid, do not modify field and return false.
11895// In case of success, return true
11896bool command::fconst_SetStrptrMaybe(command::atf_cmdline& parent, algo::strptr rhs) {
11897    bool ret = false;
11898    switch (elems_N(rhs)) {
11899        case 3: {
11900            switch (u64(algo::ReadLE16(rhs.elems))|(u64(rhs[2])<<16)) {
11901                case LE_STR3('l','o','w'): {
11902                    fconst_SetEnum(parent,command_atf_cmdline_fconst_low); ret = true; break;
11903                }
11904            }
11905            break;
11906        }
11907        case 4: {
11908            switch (u64(algo::ReadLE32(rhs.elems))) {
11909                case LE_STR4('h','i','g','h'): {
11910                    fconst_SetEnum(parent,command_atf_cmdline_fconst_high); ret = true; break;
11911                }
11912            }
11913            break;
11914        }
11915        case 6: {
11916            switch (u64(algo::ReadLE32(rhs.elems))|(u64(algo::ReadLE16(rhs.elems+4))<<32)) {
11917                case LE_STR6('m','e','d','i','u','m'): {
11918                    fconst_SetEnum(parent,command_atf_cmdline_fconst_medium); ret = true; break;
11919                }
11920            }
11921            break;
11922        }
11923    }
11924    return ret;
11925}
11926
11927// --- command.atf_cmdline.fconst.SetStrptr
11928// Convert string to field.
11929// If the string is invalid, set numeric value to DFLT
11930void command::fconst_SetStrptr(command::atf_cmdline& parent, algo::strptr rhs, command_atf_cmdline_fconst_Enum dflt) {
11931    if (!fconst_SetStrptrMaybe(parent,rhs)) fconst_SetEnum(parent,dflt);
11932}
11933
11934// --- command.atf_cmdline.fconst.ReadStrptrMaybe
11935// Convert string to field. Return success value
11936bool command::fconst_ReadStrptrMaybe(command::atf_cmdline& parent, algo::strptr rhs) {
11937    bool retval = false;
11938    retval = fconst_SetStrptrMaybe(parent,rhs); // try symbol conversion
11939    if (!retval) { // didn't work? try reading as underlying type
11940        retval = u8_ReadStrptrMaybe(parent.fconst,rhs);
11941    }
11942    return retval;
11943}
11944
11945// --- command.atf_cmdline.dregx.Print
11946// Print back to string
11947void command::dregx_Print(command::atf_cmdline& parent, algo::cstring &out) {
11948    Regx_Print(parent.dregx, out);
11949}
11950
11951// --- command.atf_cmdline.dregx.ReadStrptrMaybe
11952// Read Regx from string
11953// Convert string to field. Return success value
11954bool command::dregx_ReadStrptrMaybe(command::atf_cmdline& parent, algo::strptr in) {
11955    bool retval = true;
11956    Regx_ReadSql(parent.dregx, in, true);
11957    return retval;
11958}
11959
11960// --- command.atf_cmdline..ReadFieldMaybe
11961bool command::atf_cmdline_ReadFieldMaybe(command::atf_cmdline& parent, algo::strptr field, algo::strptr strval) {
11962    bool retval = true;
11963    command::FieldId field_id;
11964    (void)value_SetStrptrMaybe(field_id,algo::Pathcomp(field, ".LL"));
11965    switch(field_id) {
11966        case command_FieldId_in: {
11967            retval = algo::cstring_ReadStrptrMaybe(parent.in, strval);
11968            break;
11969        }
11970        case command_FieldId_exec: {
11971            retval = bool_ReadStrptrMaybe(parent.exec, strval);
11972            break;
11973        }
11974        case command_FieldId_astr: {
11975            retval = algo::cstring_ReadStrptrMaybe(parent.astr, strval);
11976            break;
11977        }
11978        case command_FieldId_anum: {
11979            retval = i32_ReadStrptrMaybe(parent.anum, strval);
11980            break;
11981        }
11982        case command_FieldId_adbl: {
11983            retval = double_ReadStrptrMaybe(parent.adbl, strval);
11984            break;
11985        }
11986        case command_FieldId_aflag: {
11987            retval = bool_ReadStrptrMaybe(parent.aflag, strval);
11988            break;
11989        }
11990        case command_FieldId_str: {
11991            retval = algo::cstring_ReadStrptrMaybe(parent.str, strval);
11992            break;
11993        }
11994        case command_FieldId_num: {
11995            retval = i32_ReadStrptrMaybe(parent.num, strval);
11996            break;
11997        }
11998        case command_FieldId_dbl: {
11999            retval = double_ReadStrptrMaybe(parent.dbl, strval);
12000            break;
12001        }
12002        case command_FieldId_flag: {
12003            retval = bool_ReadStrptrMaybe(parent.flag, strval);
12004            break;
12005        }
12006        case command_FieldId_dstr: {
12007            retval = algo::cstring_ReadStrptrMaybe(parent.dstr, strval);
12008            break;
12009        }
12010        case command_FieldId_dnum: {
12011            retval = i32_ReadStrptrMaybe(parent.dnum, strval);
12012            break;
12013        }
12014        case command_FieldId_ddbl: {
12015            retval = double_ReadStrptrMaybe(parent.ddbl, strval);
12016            break;
12017        }
12018        case command_FieldId_dflag: {
12019            retval = bool_ReadStrptrMaybe(parent.dflag, strval);
12020            break;
12021        }
12022        case command_FieldId_mstr: {
12023            retval = mstr_ReadStrptrMaybe(parent, strval);
12024            break;
12025        }
12026        case command_FieldId_mnum: {
12027            retval = mnum_ReadStrptrMaybe(parent, strval);
12028            break;
12029        }
12030        case command_FieldId_mdbl: {
12031            retval = mdbl_ReadStrptrMaybe(parent, strval);
12032            break;
12033        }
12034        case command_FieldId_amnum: {
12035            retval = amnum_ReadStrptrMaybe(parent, strval);
12036            break;
12037        }
12038        case command_FieldId_fconst: {
12039            retval = fconst_ReadStrptrMaybe(parent, strval);
12040            break;
12041        }
12042        case command_FieldId_cconst: {
12043            retval = algo::Month_ReadStrptrMaybe(parent.cconst, strval);
12044            break;
12045        }
12046        case command_FieldId_dregx: {
12047            retval = dregx_ReadStrptrMaybe(parent, strval);
12048            break;
12049        }
12050        case command_FieldId_dpkey: {
12051            retval = algo::Smallstr100_ReadStrptrMaybe(parent.dpkey, strval);
12052            break;
12053        }
12054        default: break;
12055    }
12056    if (!retval) {
12057        algo_lib::AppendErrtext("attr",field);
12058    }
12059    return retval;
12060}
12061
12062// --- command.atf_cmdline..ReadTupleMaybe
12063// Read fields of command::atf_cmdline from attributes of ascii tuple TUPLE
12064bool command::atf_cmdline_ReadTupleMaybe(command::atf_cmdline &parent, algo::Tuple &tuple) {
12065    bool retval = true;
12066    int anon_idx = 0;
12067    ind_beg(algo::Tuple_attrs_curs,attr,tuple) {
12068        if (ch_N(attr.name) == 0) {
12069            attr.name = atf_cmdline_GetAnon(parent, anon_idx++);
12070        }
12071        retval = atf_cmdline_ReadFieldMaybe(parent, attr.name, attr.value);
12072        if (!retval) {
12073            break;
12074        }
12075    }ind_end;
12076    return retval;
12077}
12078
12079// --- command.atf_cmdline..Init
12080// Set all fields to initial values.
12081void command::atf_cmdline_Init(command::atf_cmdline& parent) {
12082    parent.in = algo::strptr("data");
12083    parent.exec = bool(false);
12084    parent.anum = i32(0);
12085    parent.adbl = double(0.0);
12086    parent.aflag = bool(false);
12087    parent.num = i32(0);
12088    parent.dbl = double(0.0);
12089    parent.flag = bool(false);
12090    parent.dstr = algo::strptr("blah");
12091    parent.dnum = i32(-33);
12092    parent.ddbl = double(0.0001);
12093    parent.dflag = bool(true);
12094    parent.mstr_elems 	= 0; // (command.atf_cmdline.mstr)
12095    parent.mstr_n     	= 0; // (command.atf_cmdline.mstr)
12096    parent.mstr_max   	= 0; // (command.atf_cmdline.mstr)
12097    parent.mnum_elems 	= 0; // (command.atf_cmdline.mnum)
12098    parent.mnum_n     	= 0; // (command.atf_cmdline.mnum)
12099    parent.mnum_max   	= 0; // (command.atf_cmdline.mnum)
12100    parent.mdbl_elems 	= 0; // (command.atf_cmdline.mdbl)
12101    parent.mdbl_n     	= 0; // (command.atf_cmdline.mdbl)
12102    parent.mdbl_max   	= 0; // (command.atf_cmdline.mdbl)
12103    parent.amnum_elems 	= 0; // (command.atf_cmdline.amnum)
12104    parent.amnum_n     	= 0; // (command.atf_cmdline.amnum)
12105    parent.amnum_max   	= 0; // (command.atf_cmdline.amnum)
12106    parent.fconst = u8(0);
12107    Regx_ReadSql(parent.dregx, "%", true);
12108    parent.dpkey = algo::strptr("");
12109}
12110
12111// --- command.atf_cmdline..Uninit
12112void command::atf_cmdline_Uninit(command::atf_cmdline& parent) {
12113    command::atf_cmdline &row = parent; (void)row;
12114
12115    // command.atf_cmdline.amnum.Uninit (Tary)  //Anon number array
12116    // remove all elements from command.atf_cmdline.amnum
12117    amnum_RemoveAll(parent);
12118    // free memory for Tary command.atf_cmdline.amnum
12119    algo_lib::malloc_FreeMem(parent.amnum_elems, sizeof(i32)*parent.amnum_max); // (command.atf_cmdline.amnum)
12120
12121    // command.atf_cmdline.mdbl.Uninit (Tary)  //Double array
12122    // remove all elements from command.atf_cmdline.mdbl
12123    mdbl_RemoveAll(parent);
12124    // free memory for Tary command.atf_cmdline.mdbl
12125    algo_lib::malloc_FreeMem(parent.mdbl_elems, sizeof(double)*parent.mdbl_max); // (command.atf_cmdline.mdbl)
12126
12127    // command.atf_cmdline.mnum.Uninit (Tary)  //Number array
12128    // remove all elements from command.atf_cmdline.mnum
12129    mnum_RemoveAll(parent);
12130    // free memory for Tary command.atf_cmdline.mnum
12131    algo_lib::malloc_FreeMem(parent.mnum_elems, sizeof(i32)*parent.mnum_max); // (command.atf_cmdline.mnum)
12132
12133    // command.atf_cmdline.mstr.Uninit (Tary)  //String array
12134    // remove all elements from command.atf_cmdline.mstr
12135    mstr_RemoveAll(parent);
12136    // free memory for Tary command.atf_cmdline.mstr
12137    algo_lib::malloc_FreeMem(parent.mstr_elems, sizeof(algo::cstring)*parent.mstr_max); // (command.atf_cmdline.mstr)
12138}
12139
12140// --- command.atf_cmdline..ToCmdline
12141// Convenience function that returns a full command line
12142// Assume command is in a directory called bin
12143tempstr command::atf_cmdline_ToCmdline(command::atf_cmdline& row) {
12144    tempstr ret;
12145    ret << "bin/atf_cmdline ";
12146    atf_cmdline_PrintArgv(row, ret);
12147    // inherit less intense verbose, debug options
12148    for (int i = 1; i < algo_lib::_db.cmdline.verbose; i++) {
12149        ret << " -verbose";
12150    }
12151    for (int i = 1; i < algo_lib::_db.cmdline.debug; i++) {
12152        ret << " -debug";
12153    }
12154    return ret;
12155}
12156
12157// --- command.atf_cmdline..PrintArgv
12158// print string representation of ROW to string STR
12159// cfmt:command.atf_cmdline.Argv  printfmt:Tuple
12160void command::atf_cmdline_PrintArgv(command::atf_cmdline& row, algo::cstring& str) {
12161    algo::tempstr temp;
12162    (void)temp;
12163    (void)str;
12164    if (!(row.in == "data")) {
12165        ch_RemoveAll(temp);
12166        cstring_Print(row.in, temp);
12167        str << " -in:";
12168        strptr_PrintBash(temp,str);
12169    }
12170    if (!(row.exec == false)) {
12171        ch_RemoveAll(temp);
12172        bool_Print(row.exec, temp);
12173        str << " -exec:";
12174        strptr_PrintBash(temp,str);
12175    }
12176    ch_RemoveAll(temp);
12177    cstring_Print(row.astr, temp);
12178    str << " -astr:";
12179    strptr_PrintBash(temp,str);
12180    ch_RemoveAll(temp);
12181    i32_Print(row.anum, temp);
12182    str << " -anum:";
12183    strptr_PrintBash(temp,str);
12184    ch_RemoveAll(temp);
12185    double_Print(row.adbl, temp);
12186    str << " -adbl:";
12187    strptr_PrintBash(temp,str);
12188    ch_RemoveAll(temp);
12189    bool_Print(row.aflag, temp);
12190    str << " -aflag:";
12191    strptr_PrintBash(temp,str);
12192    ch_RemoveAll(temp);
12193    cstring_Print(row.str, temp);
12194    str << " -str:";
12195    strptr_PrintBash(temp,str);
12196    if (!(row.num == 0)) {
12197        ch_RemoveAll(temp);
12198        i32_Print(row.num, temp);
12199        str << " -num:";
12200        strptr_PrintBash(temp,str);
12201    }
12202    if (!(row.dbl == 0.0)) {
12203        ch_RemoveAll(temp);
12204        double_Print(row.dbl, temp);
12205        str << " -dbl:";
12206        strptr_PrintBash(temp,str);
12207    }
12208    if (!(row.flag == false)) {
12209        ch_RemoveAll(temp);
12210        bool_Print(row.flag, temp);
12211        str << " -flag:";
12212        strptr_PrintBash(temp,str);
12213    }
12214    if (!(row.dstr == "blah")) {
12215        ch_RemoveAll(temp);
12216        cstring_Print(row.dstr, temp);
12217        str << " -dstr:";
12218        strptr_PrintBash(temp,str);
12219    }
12220    if (!(row.dnum == -33)) {
12221        ch_RemoveAll(temp);
12222        i32_Print(row.dnum, temp);
12223        str << " -dnum:";
12224        strptr_PrintBash(temp,str);
12225    }
12226    if (!(row.ddbl == 0.0001)) {
12227        ch_RemoveAll(temp);
12228        double_Print(row.ddbl, temp);
12229        str << " -ddbl:";
12230        strptr_PrintBash(temp,str);
12231    }
12232    if (!(row.dflag == true)) {
12233        ch_RemoveAll(temp);
12234        bool_Print(row.dflag, temp);
12235        str << " -dflag:";
12236        strptr_PrintBash(temp,str);
12237    }
12238    ind_beg(atf_cmdline_mstr_curs,value,row) {
12239        ch_RemoveAll(temp);
12240        cstring_Print(value, temp);
12241        str << " -mstr:";
12242        strptr_PrintBash(temp,str);
12243    }ind_end;
12244    ind_beg(atf_cmdline_mnum_curs,value,row) {
12245        ch_RemoveAll(temp);
12246        i32_Print(value, temp);
12247        str << " -mnum:";
12248        strptr_PrintBash(temp,str);
12249    }ind_end;
12250    ind_beg(atf_cmdline_mdbl_curs,value,row) {
12251        ch_RemoveAll(temp);
12252        double_Print(value, temp);
12253        str << " -mdbl:";
12254        strptr_PrintBash(temp,str);
12255    }ind_end;
12256    ind_beg(atf_cmdline_amnum_curs,value,row) {
12257        ch_RemoveAll(temp);
12258        i32_Print(value, temp);
12259        str << " -amnum:";
12260        strptr_PrintBash(temp,str);
12261    }ind_end;
12262    if (!(row.fconst == 0)) {
12263        ch_RemoveAll(temp);
12264        command::fconst_Print(const_cast<command::atf_cmdline&>(row), temp);
12265        str << " -fconst:";
12266        strptr_PrintBash(temp,str);
12267    }
12268    ch_RemoveAll(temp);
12269    Month_Print(row.cconst, temp);
12270    str << " -cconst:";
12271    strptr_PrintBash(temp,str);
12272    if (!(row.dregx.expr == "%")) {
12273        ch_RemoveAll(temp);
12274        command::dregx_Print(const_cast<command::atf_cmdline&>(row), temp);
12275        str << " -dregx:";
12276        strptr_PrintBash(temp,str);
12277    }
12278    if (!(row.dpkey == "")) {
12279        ch_RemoveAll(temp);
12280        Smallstr100_Print(row.dpkey, temp);
12281        str << " -dpkey:";
12282        strptr_PrintBash(temp,str);
12283    }
12284}
12285
12286// --- command.atf_cmdline..GetAnon
12287algo::strptr command::atf_cmdline_GetAnon(command::atf_cmdline &parent, i32 idx) {
12288    (void)parent;//only to avoid -Wunused-parameter
12289    switch(idx) {
12290        case(0): return strptr("astr", 4);
12291        case(1): return strptr("anum", 4);
12292        case(2): return strptr("adbl", 4);
12293        case(3): return strptr("aflag", 5);
12294        default: return strptr("amnum", 5);
12295    }
12296}
12297
12298// --- command.atf_cmdline..NArgs
12299// Used with command lines
12300// Return # of command-line arguments that must follow this argument
12301// If FIELD is invalid, return -1
12302i32 command::atf_cmdline_NArgs(command::FieldId field, algo::strptr& out_dflt, bool* out_anon) {
12303    i32 retval = 1;
12304    switch (field) {
12305        case command_FieldId_in: { // $comment
12306            *out_anon = false;
12307        } break;
12308        case command_FieldId_exec: { // $comment
12309            *out_anon = false;
12310            retval=0;
12311            out_dflt="Y";
12312        } break;
12313        case command_FieldId_astr: { // bool: no argument required but value may be specified as exec:Y
12314            *out_anon = true;
12315        } break;
12316        case command_FieldId_anum: { // bool: no argument required but value may be specified as exec:Y
12317            *out_anon = true;
12318        } break;
12319        case command_FieldId_adbl: { // bool: no argument required but value may be specified as exec:Y
12320            *out_anon = true;
12321        } break;
12322        case command_FieldId_aflag: { // bool: no argument required but value may be specified as exec:Y
12323            *out_anon = true;
12324            retval=0;
12325            out_dflt="Y";
12326        } break;
12327        case command_FieldId_str: { // bool: no argument required but value may be specified as aflag:Y
12328            *out_anon = false;
12329        } break;
12330        case command_FieldId_num: { // bool: no argument required but value may be specified as aflag:Y
12331            *out_anon = false;
12332        } break;
12333        case command_FieldId_dbl: { // bool: no argument required but value may be specified as aflag:Y
12334            *out_anon = false;
12335        } break;
12336        case command_FieldId_flag: { // bool: no argument required but value may be specified as aflag:Y
12337            *out_anon = false;
12338            retval=0;
12339            out_dflt="Y";
12340        } break;
12341        case command_FieldId_dstr: { // bool: no argument required but value may be specified as flag:Y
12342            *out_anon = false;
12343        } break;
12344        case command_FieldId_dnum: { // bool: no argument required but value may be specified as flag:Y
12345            *out_anon = false;
12346        } break;
12347        case command_FieldId_ddbl: { // bool: no argument required but value may be specified as flag:Y
12348            *out_anon = false;
12349        } break;
12350        case command_FieldId_dflag: { // bool: no argument required but value may be specified as flag:Y
12351            *out_anon = false;
12352            retval=0;
12353            out_dflt="Y";
12354        } break;
12355        case command_FieldId_mstr: { // bool: no argument required but value may be specified as dflag:Y
12356            *out_anon = false;
12357        } break;
12358        case command_FieldId_mnum: { // bool: no argument required but value may be specified as dflag:Y
12359            *out_anon = false;
12360        } break;
12361        case command_FieldId_mdbl: { // bool: no argument required but value may be specified as dflag:Y
12362            *out_anon = false;
12363        } break;
12364        case command_FieldId_amnum: { // bool: no argument required but value may be specified as dflag:Y
12365            *out_anon = true;
12366        } break;
12367        case command_FieldId_fconst: { // bool: no argument required but value may be specified as dflag:Y
12368            *out_anon = false;
12369        } break;
12370        case command_FieldId_cconst: { // bool: no argument required but value may be specified as dflag:Y
12371            *out_anon = false;
12372        } break;
12373        case command_FieldId_dregx: { // bool: no argument required but value may be specified as dflag:Y
12374            *out_anon = false;
12375        } break;
12376        case command_FieldId_dpkey: { // bool: no argument required but value may be specified as dflag:Y
12377            *out_anon = false;
12378        } break;
12379        default:
12380        retval=-1; // unrecognized
12381    }
12382    return retval;
12383}
12384
12385// --- command.atf_cmdline_proc.atf_cmdline.Start
12386// Start subprocess
12387// If subprocess already running, do nothing. Otherwise, start it
12388int command::atf_cmdline_Start(command::atf_cmdline_proc& parent) {
12389    int retval = 0;
12390    if (parent.pid == 0) {
12391        verblog(atf_cmdline_ToCmdline(parent)); // maybe print command
12392#ifdef WIN32
12393        algo_lib::ResolveExecFname(parent.path);
12394        tempstr cmdline(atf_cmdline_ToCmdline(parent));
12395        parent.pid = dospawn(Zeroterm(parent.path),Zeroterm(cmdline),parent.timeout,parent.fstdin,parent.fstdout,parent.fstderr);
12396#else
12397        parent.pid = fork();
12398        if (parent.pid == 0) { // child
12399            algo_lib::DieWithParent();
12400            if (parent.timeout > 0) {
12401                alarm(parent.timeout);
12402            }
12403            if (retval==0) retval=algo_lib::ApplyRedirect(parent.fstdin , 0);
12404            if (retval==0) retval=algo_lib::ApplyRedirect(parent.fstdout, 1);
12405            if (retval==0) retval=algo_lib::ApplyRedirect(parent.fstderr, 2);
12406            if (retval==0) retval= atf_cmdline_Execv(parent);
12407            if (retval != 0) { // if start fails, print error
12408                int err=errno;
12409                prerr("command.atf_cmdline_execv"
12410                <<Keyval("errno",err)
12411                <<Keyval("errstr",strerror(err))
12412                <<Keyval("comment","Execv failed"));
12413            }
12414            _exit(127); // if failed to start, exit anyway
12415        } else if (parent.pid == -1) {
12416            retval = errno; // failed to fork
12417        }
12418#endif
12419    }
12420    parent.status = parent.pid > 0 ? 0 : -1; // if didn't start, set error status
12421    return retval;
12422}
12423
12424// --- command.atf_cmdline_proc.atf_cmdline.StartRead
12425// Start subprocess & Read output
12426algo::Fildes command::atf_cmdline_StartRead(command::atf_cmdline_proc& parent, algo_lib::FFildes &read) {
12427    int pipefd[2];
12428    int rc=pipe(pipefd);
12429    (void)rc;
12430    read.fd.value = pipefd[0];
12431    parent.fstdout  << ">&" << pipefd[1];
12432    atf_cmdline_Start(parent);
12433    (void)close(pipefd[1]);
12434    return read.fd;
12435}
12436
12437// --- command.atf_cmdline_proc.atf_cmdline.Kill
12438// Kill subprocess and wait
12439void command::atf_cmdline_Kill(command::atf_cmdline_proc& parent) {
12440    if (parent.pid != 0) {
12441        kill(parent.pid,9);
12442        atf_cmdline_Wait(parent);
12443    }
12444}
12445
12446// --- command.atf_cmdline_proc.atf_cmdline.Wait
12447// Wait for subprocess to return
12448void command::atf_cmdline_Wait(command::atf_cmdline_proc& parent) {
12449    if (parent.pid > 0) {
12450        int wait_flags = 0;
12451        int wait_status = 0;
12452        int rc = -1;
12453        do {
12454            // really wait for subprocess to exit
12455            rc = waitpid(parent.pid,&wait_status,wait_flags);
12456        } while (rc==-1 && errno==EINTR);
12457        if (rc == parent.pid) {
12458            parent.status = wait_status;
12459            parent.pid = 0;
12460        }
12461    }
12462}
12463
12464// --- command.atf_cmdline_proc.atf_cmdline.Exec
12465// Start + Wait
12466// Execute subprocess and return exit code
12467int command::atf_cmdline_Exec(command::atf_cmdline_proc& parent) {
12468    atf_cmdline_Start(parent);
12469    atf_cmdline_Wait(parent);
12470    return parent.status;
12471}
12472
12473// --- command.atf_cmdline_proc.atf_cmdline.ExecX
12474// Start + Wait, throw exception on error
12475// Execute subprocess; throw human-readable exception on error
12476void command::atf_cmdline_ExecX(command::atf_cmdline_proc& parent) {
12477    int rc = atf_cmdline_Exec(parent);
12478    vrfy(rc==0, tempstr() << "algo_lib.exec" << Keyval("cmd",atf_cmdline_ToCmdline(parent))
12479    << Keyval("comment",algo::DescribeWaitStatus(parent.status)));
12480}
12481
12482// --- command.atf_cmdline_proc.atf_cmdline.Execv
12483// Call execv()
12484// Call execv with specified parameters
12485int command::atf_cmdline_Execv(command::atf_cmdline_proc& parent) {
12486    int ret = 0;
12487    algo::StringAry args;
12488    atf_cmdline_ToArgv(parent, args);
12489    char **argv = (char**)alloca((ary_N(args)+1)*sizeof(*argv));
12490    ind_beg(algo::StringAry_ary_curs,arg,args) {
12491        argv[ind_curs(arg).index] = Zeroterm(arg);
12492    }ind_end;
12493    argv[ary_N(args)] = NULL;
12494    // if parent.path is relative, search for it in PATH
12495    algo_lib::ResolveExecFname(parent.path);
12496    ret = execv(Zeroterm(parent.path),argv);
12497    return ret;
12498}
12499
12500// --- command.atf_cmdline_proc.atf_cmdline.ToCmdline
12501algo::tempstr command::atf_cmdline_ToCmdline(command::atf_cmdline_proc& parent) {
12502    algo::tempstr retval;
12503    retval << parent.path << " ";
12504    command::atf_cmdline_PrintArgv(parent.cmd,retval);
12505    if (ch_N(parent.fstdin)) {
12506        retval << " " << parent.fstdin;
12507    }
12508    if (ch_N(parent.fstdout)) {
12509        retval << " " << parent.fstdout;
12510    }
12511    if (ch_N(parent.fstderr)) {
12512        retval << " 2" << parent.fstderr;
12513    }
12514    return retval;
12515}
12516
12517// --- command.atf_cmdline_proc.atf_cmdline.ToArgv
12518// Form array from the command line
12519void command::atf_cmdline_ToArgv(command::atf_cmdline_proc& parent, algo::StringAry& args) {
12520    ary_RemoveAll(args);
12521    ary_Alloc(args) << parent.path;
12522
12523    if (parent.cmd.in != "data") {
12524        cstring *arg = &ary_Alloc(args);
12525        *arg << "-in:";
12526        cstring_Print(parent.cmd.in, *arg);
12527    }
12528
12529    if (parent.cmd.exec != false) {
12530        cstring *arg = &ary_Alloc(args);
12531        *arg << "-exec:";
12532        bool_Print(parent.cmd.exec, *arg);
12533    }
12534
12535    if (true) {
12536        cstring *arg = &ary_Alloc(args);
12537        *arg << "-astr:";
12538        cstring_Print(parent.cmd.astr, *arg);
12539    }
12540
12541    if (parent.cmd.anum != 0) {
12542        cstring *arg = &ary_Alloc(args);
12543        *arg << "-anum:";
12544        i32_Print(parent.cmd.anum, *arg);
12545    }
12546
12547    if (parent.cmd.adbl != 0.0) {
12548        cstring *arg = &ary_Alloc(args);
12549        *arg << "-adbl:";
12550        double_Print(parent.cmd.adbl, *arg);
12551    }
12552
12553    if (parent.cmd.aflag != false) {
12554        cstring *arg = &ary_Alloc(args);
12555        *arg << "-aflag:";
12556        bool_Print(parent.cmd.aflag, *arg);
12557    }
12558
12559    if (true) {
12560        cstring *arg = &ary_Alloc(args);
12561        *arg << "-str:";
12562        cstring_Print(parent.cmd.str, *arg);
12563    }
12564
12565    if (parent.cmd.num != 0) {
12566        cstring *arg = &ary_Alloc(args);
12567        *arg << "-num:";
12568        i32_Print(parent.cmd.num, *arg);
12569    }
12570
12571    if (parent.cmd.dbl != 0.0) {
12572        cstring *arg = &ary_Alloc(args);
12573        *arg << "-dbl:";
12574        double_Print(parent.cmd.dbl, *arg);
12575    }
12576
12577    if (parent.cmd.flag != false) {
12578        cstring *arg = &ary_Alloc(args);
12579        *arg << "-flag:";
12580        bool_Print(parent.cmd.flag, *arg);
12581    }
12582
12583    if (parent.cmd.dstr != "blah") {
12584        cstring *arg = &ary_Alloc(args);
12585        *arg << "-dstr:";
12586        cstring_Print(parent.cmd.dstr, *arg);
12587    }
12588
12589    if (parent.cmd.dnum != -33) {
12590        cstring *arg = &ary_Alloc(args);
12591        *arg << "-dnum:";
12592        i32_Print(parent.cmd.dnum, *arg);
12593    }
12594
12595    if (parent.cmd.ddbl != 0.0001) {
12596        cstring *arg = &ary_Alloc(args);
12597        *arg << "-ddbl:";
12598        double_Print(parent.cmd.ddbl, *arg);
12599    }
12600
12601    if (parent.cmd.dflag != true) {
12602        cstring *arg = &ary_Alloc(args);
12603        *arg << "-dflag:";
12604        bool_Print(parent.cmd.dflag, *arg);
12605    }
12606    ind_beg(command::atf_cmdline_mstr_curs,value,parent.cmd) {
12607        cstring *arg = &ary_Alloc(args);
12608        *arg << "-mstr:";
12609        cstring_Print(value, *arg);
12610    }ind_end;
12611    ind_beg(command::atf_cmdline_mnum_curs,value,parent.cmd) {
12612        cstring *arg = &ary_Alloc(args);
12613        *arg << "-mnum:";
12614        i32_Print(value, *arg);
12615    }ind_end;
12616    ind_beg(command::atf_cmdline_mdbl_curs,value,parent.cmd) {
12617        cstring *arg = &ary_Alloc(args);
12618        *arg << "-mdbl:";
12619        double_Print(value, *arg);
12620    }ind_end;
12621    ind_beg(command::atf_cmdline_amnum_curs,value,parent.cmd) {
12622        cstring *arg = &ary_Alloc(args);
12623        *arg << "-amnum:";
12624        i32_Print(value, *arg);
12625    }ind_end;
12626
12627    if (parent.cmd.fconst != 0) {
12628        cstring *arg = &ary_Alloc(args);
12629        *arg << "-fconst:";
12630        command::fconst_Print(parent.cmd, *arg);
12631    }
12632
12633    if (true) {
12634        cstring *arg = &ary_Alloc(args);
12635        *arg << "-cconst:";
12636        Month_Print(parent.cmd.cconst, *arg);
12637    }
12638
12639    if (parent.cmd.dregx.expr != "%") {
12640        cstring *arg = &ary_Alloc(args);
12641        *arg << "-dregx:";
12642        command::dregx_Print(parent.cmd, *arg);
12643    }
12644
12645    if (parent.cmd.dpkey != "") {
12646        cstring *arg = &ary_Alloc(args);
12647        *arg << "-dpkey:";
12648        Smallstr100_Print(parent.cmd.dpkey, *arg);
12649    }
12650    for (int i=1; i < algo_lib::_db.cmdline.verbose; ++i) {
12651        ary_Alloc(args) << "-verbose";
12652    }
12653}
12654
12655// --- command.atf_cmdline_proc..Uninit
12656void command::atf_cmdline_proc_Uninit(command::atf_cmdline_proc& parent) {
12657    command::atf_cmdline_proc &row = parent; (void)row;
12658
12659    // command.atf_cmdline_proc.atf_cmdline.Uninit (Exec)  //
12660    atf_cmdline_Kill(parent); // kill child, ensure forward progress
12661}
12662
12663// --- command.atf_comp.comptest.Print
12664// Print back to string
12665void command::comptest_Print(command::atf_comp& parent, algo::cstring &out) {
12666    Regx_Print(parent.comptest, out);
12667}
12668
12669// --- command.atf_comp.comptest.ReadStrptrMaybe
12670// Read Regx from string
12671// Convert string to field. Return success value
12672bool command::comptest_ReadStrptrMaybe(command::atf_comp& parent, algo::strptr in) {
12673    bool retval = true;
12674    Regx_ReadSql(parent.comptest, in, true);
12675    return retval;
12676}
12677
12678// --- command.atf_comp..ReadFieldMaybe
12679bool command::atf_comp_ReadFieldMaybe(command::atf_comp& parent, algo::strptr field, algo::strptr strval) {
12680    bool retval = true;
12681    command::FieldId field_id;
12682    (void)value_SetStrptrMaybe(field_id,field);
12683    switch(field_id) {
12684        case command_FieldId_in: {
12685            retval = algo::cstring_ReadStrptrMaybe(parent.in, strval);
12686            break;
12687        }
12688        case command_FieldId_comptest: {
12689            retval = comptest_ReadStrptrMaybe(parent, strval);
12690            break;
12691        }
12692        case command_FieldId_mdbg: {
12693            retval = bool_ReadStrptrMaybe(parent.mdbg, strval);
12694            break;
12695        }
12696        case command_FieldId_run: {
12697            retval = bool_ReadStrptrMaybe(parent.run, strval);
12698            break;
12699        }
12700        case command_FieldId_capture: {
12701            retval = bool_ReadStrptrMaybe(parent.capture, strval);
12702            break;
12703        }
12704        case command_FieldId_print: {
12705            retval = bool_ReadStrptrMaybe(parent.print, strval);
12706            break;
12707        }
12708        case command_FieldId_printinput: {
12709            retval = bool_ReadStrptrMaybe(parent.printinput, strval);
12710            break;
12711        }
12712        case command_FieldId_e: {
12713            retval = bool_ReadStrptrMaybe(parent.e, strval);
12714            break;
12715        }
12716        case command_FieldId_normalize: {
12717            retval = bool_ReadStrptrMaybe(parent.normalize, strval);
12718            break;
12719        }
12720        case command_FieldId_covcapture: {
12721            retval = bool_ReadStrptrMaybe(parent.covcapture, strval);
12722            break;
12723        }
12724        case command_FieldId_covcheck: {
12725            retval = bool_ReadStrptrMaybe(parent.covcheck, strval);
12726            break;
12727        }
12728        case command_FieldId_compdir: {
12729            retval = algo::cstring_ReadStrptrMaybe(parent.compdir, strval);
12730            break;
12731        }
12732        case command_FieldId_cfg: {
12733            retval = algo::Smallstr50_ReadStrptrMaybe(parent.cfg, strval);
12734            break;
12735        }
12736        case command_FieldId_check_untracked: {
12737            retval = bool_ReadStrptrMaybe(parent.check_untracked, strval);
12738            break;
12739        }
12740        case command_FieldId_maxerr: {
12741            retval = i32_ReadStrptrMaybe(parent.maxerr, strval);
12742            break;
12743        }
12744        case command_FieldId_build: {
12745            retval = bool_ReadStrptrMaybe(parent.build, strval);
12746            break;
12747        }
12748        case command_FieldId_ood: {
12749            retval = bool_ReadStrptrMaybe(parent.ood, strval);
12750            break;
12751        }
12752        case command_FieldId_memcheck: {
12753            retval = bool_ReadStrptrMaybe(parent.memcheck, strval);
12754            break;
12755        }
12756        case command_FieldId_force: {
12757            retval = bool_ReadStrptrMaybe(parent.force, strval);
12758            break;
12759        }
12760        case command_FieldId_callgrind: {
12761            retval = bool_ReadStrptrMaybe(parent.callgrind, strval);
12762            break;
12763        }
12764        case command_FieldId_maxjobs: {
12765            retval = i32_ReadStrptrMaybe(parent.maxjobs, strval);
12766            break;
12767        }
12768        case command_FieldId_stream: {
12769            retval = bool_ReadStrptrMaybe(parent.stream, strval);
12770            break;
12771        }
12772        case command_FieldId_i: {
12773            retval = bool_ReadStrptrMaybe(parent.i, strval);
12774            break;
12775        }
12776        case command_FieldId_write: {
12777            retval = bool_ReadStrptrMaybe(parent.write, strval);
12778            break;
12779        }
12780        case command_FieldId_report: {
12781            retval = bool_ReadStrptrMaybe(parent.report, strval);
12782            break;
12783        }
12784        case command_FieldId_b: {
12785            retval = algo::cstring_ReadStrptrMaybe(parent.b, strval);
12786            break;
12787        }
12788        default: break;
12789    }
12790    if (!retval) {
12791        algo_lib::AppendErrtext("attr",field);
12792    }
12793    return retval;
12794}
12795
12796// --- command.atf_comp..ReadTupleMaybe
12797// Read fields of command::atf_comp from attributes of ascii tuple TUPLE
12798bool command::atf_comp_ReadTupleMaybe(command::atf_comp &parent, algo::Tuple &tuple) {
12799    bool retval = true;
12800    int anon_idx = 0;
12801    ind_beg(algo::Tuple_attrs_curs,attr,tuple) {
12802        if (ch_N(attr.name) == 0) {
12803            attr.name = atf_comp_GetAnon(parent, anon_idx++);
12804        }
12805        retval = atf_comp_ReadFieldMaybe(parent, attr.name, attr.value);
12806        if (!retval) {
12807            break;
12808        }
12809    }ind_end;
12810    return retval;
12811}
12812
12813// --- command.atf_comp..Init
12814// Set all fields to initial values.
12815void command::atf_comp_Init(command::atf_comp& parent) {
12816    parent.in = algo::strptr("data");
12817    Regx_ReadSql(parent.comptest, "%", true);
12818    parent.mdbg = bool(false);
12819    parent.run = bool(true);
12820    parent.capture = bool(false);
12821    parent.print = bool(false);
12822    parent.printinput = bool(false);
12823    parent.e = bool(false);
12824    parent.normalize = bool(false);
12825    parent.covcapture = bool(false);
12826    parent.covcheck = bool(false);
12827    parent.compdir = algo::strptr("");
12828    parent.cfg = algo::strptr("release");
12829    parent.check_untracked = bool(true);
12830    parent.maxerr = i32(1);
12831    parent.build = bool(false);
12832    parent.ood = bool(false);
12833    parent.memcheck = bool(false);
12834    parent.force = bool(false);
12835    parent.callgrind = bool(false);
12836    parent.maxjobs = i32(0);
12837    parent.stream = bool(false);
12838    parent.i = bool(false);
12839    parent.write = bool(true);
12840    parent.report = bool(false);
12841    parent.b = algo::strptr("");
12842}
12843
12844// --- command.atf_comp..ToCmdline
12845// Convenience function that returns a full command line
12846// Assume command is in a directory called bin
12847tempstr command::atf_comp_ToCmdline(command::atf_comp& row) {
12848    tempstr ret;
12849    ret << "bin/atf_comp ";
12850    atf_comp_PrintArgv(row, ret);
12851    // inherit less intense verbose, debug options
12852    for (int i = 1; i < algo_lib::_db.cmdline.verbose; i++) {
12853        ret << " -verbose";
12854    }
12855    for (int i = 1; i < algo_lib::_db.cmdline.debug; i++) {
12856        ret << " -debug";
12857    }
12858    return ret;
12859}
12860
12861// --- command.atf_comp..PrintArgv
12862// print string representation of ROW to string STR
12863// cfmt:command.atf_comp.Argv  printfmt:Tuple
12864void command::atf_comp_PrintArgv(command::atf_comp& row, algo::cstring& str) {
12865    algo::tempstr temp;
12866    (void)temp;
12867    (void)str;
12868    if (!(row.in == "data")) {
12869        ch_RemoveAll(temp);
12870        cstring_Print(row.in, temp);
12871        str << " -in:";
12872        strptr_PrintBash(temp,str);
12873    }
12874    ch_RemoveAll(temp);
12875    command::comptest_Print(const_cast<command::atf_comp&>(row), temp);
12876    str << " -comptest:";
12877    strptr_PrintBash(temp,str);
12878    if (!(row.mdbg == false)) {
12879        ch_RemoveAll(temp);
12880        bool_Print(row.mdbg, temp);
12881        str << " -mdbg:";
12882        strptr_PrintBash(temp,str);
12883    }
12884    if (!(row.run == true)) {
12885        ch_RemoveAll(temp);
12886        bool_Print(row.run, temp);
12887        str << " -run:";
12888        strptr_PrintBash(temp,str);
12889    }
12890    if (!(row.capture == false)) {
12891        ch_RemoveAll(temp);
12892        bool_Print(row.capture, temp);
12893        str << " -capture:";
12894        strptr_PrintBash(temp,str);
12895    }
12896    if (!(row.print == false)) {
12897        ch_RemoveAll(temp);
12898        bool_Print(row.print, temp);
12899        str << " -print:";
12900        strptr_PrintBash(temp,str);
12901    }
12902    if (!(row.printinput == false)) {
12903        ch_RemoveAll(temp);
12904        bool_Print(row.printinput, temp);
12905        str << " -printinput:";
12906        strptr_PrintBash(temp,str);
12907    }
12908    if (!(row.e == false)) {
12909        ch_RemoveAll(temp);
12910        bool_Print(row.e, temp);
12911        str << " -e:";
12912        strptr_PrintBash(temp,str);
12913    }
12914    if (!(row.normalize == false)) {
12915        ch_RemoveAll(temp);
12916        bool_Print(row.normalize, temp);
12917        str << " -normalize:";
12918        strptr_PrintBash(temp,str);
12919    }
12920    if (!(row.covcapture == false)) {
12921        ch_RemoveAll(temp);
12922        bool_Print(row.covcapture, temp);
12923        str << " -covcapture:";
12924        strptr_PrintBash(temp,str);
12925    }
12926    if (!(row.covcheck == false)) {
12927        ch_RemoveAll(temp);
12928        bool_Print(row.covcheck, temp);
12929        str << " -covcheck:";
12930        strptr_PrintBash(temp,str);
12931    }
12932    if (!(row.compdir == "")) {
12933        ch_RemoveAll(temp);
12934        cstring_Print(row.compdir, temp);
12935        str << " -compdir:";
12936        strptr_PrintBash(temp,str);
12937    }
12938    if (!(row.cfg == "release")) {
12939        ch_RemoveAll(temp);
12940        Smallstr50_Print(row.cfg, temp);
12941        str << " -cfg:";
12942        strptr_PrintBash(temp,str);
12943    }
12944    if (!(row.check_untracked == true)) {
12945        ch_RemoveAll(temp);
12946        bool_Print(row.check_untracked, temp);
12947        str << " -check_untracked:";
12948        strptr_PrintBash(temp,str);
12949    }
12950    if (!(row.maxerr == 1)) {
12951        ch_RemoveAll(temp);
12952        i32_Print(row.maxerr, temp);
12953        str << " -maxerr:";
12954        strptr_PrintBash(temp,str);
12955    }
12956    if (!(row.build == false)) {
12957        ch_RemoveAll(temp);
12958        bool_Print(row.build, temp);
12959        str << " -build:";
12960        strptr_PrintBash(temp,str);
12961    }
12962    if (!(row.ood == false)) {
12963        ch_RemoveAll(temp);
12964        bool_Print(row.ood, temp);
12965        str << " -ood:";
12966        strptr_PrintBash(temp,str);
12967    }
12968    if (!(row.memcheck == false)) {
12969        ch_RemoveAll(temp);
12970        bool_Print(row.memcheck, temp);
12971        str << " -memcheck:";
12972        strptr_PrintBash(temp,str);
12973    }
12974    if (!(row.force == false)) {
12975        ch_RemoveAll(temp);
12976        bool_Print(row.force, temp);
12977        str << " -force:";
12978        strptr_PrintBash(temp,str);
12979    }
12980    if (!(row.callgrind == false)) {
12981        ch_RemoveAll(temp);
12982        bool_Print(row.callgrind, temp);
12983        str << " -callgrind:";
12984        strptr_PrintBash(temp,str);
12985    }
12986    if (!(row.maxjobs == 0)) {
12987        ch_RemoveAll(temp);
12988        i32_Print(row.maxjobs, temp);
12989        str << " -maxjobs:";
12990        strptr_PrintBash(temp,str);
12991    }
12992    if (!(row.stream == false)) {
12993        ch_RemoveAll(temp);
12994        bool_Print(row.stream, temp);
12995        str << " -stream:";
12996        strptr_PrintBash(temp,str);
12997    }
12998    if (!(row.i == false)) {
12999        ch_RemoveAll(temp);
13000        bool_Print(row.i, temp);
13001        str << " -i:";
13002        strptr_PrintBash(temp,str);
13003    }
13004    if (!(row.write == true)) {
13005        ch_RemoveAll(temp);
13006        bool_Print(row.write, temp);
13007        str << " -write:";
13008        strptr_PrintBash(temp,str);
13009    }
13010    if (!(row.report == false)) {
13011        ch_RemoveAll(temp);
13012        bool_Print(row.report, temp);
13013        str << " -report:";
13014        strptr_PrintBash(temp,str);
13015    }
13016    if (!(row.b == "")) {
13017        ch_RemoveAll(temp);
13018        cstring_Print(row.b, temp);
13019        str << " -b:";
13020        strptr_PrintBash(temp,str);
13021    }
13022}
13023
13024// --- command.atf_comp..GetAnon
13025algo::strptr command::atf_comp_GetAnon(command::atf_comp &parent, i32 idx) {
13026    (void)parent;//only to avoid -Wunused-parameter
13027    switch(idx) {
13028        case(0): return strptr("comptest", 8);
13029        default: return algo::strptr();
13030    }
13031}
13032
13033// --- command.atf_comp..NArgs
13034// Used with command lines
13035// Return # of command-line arguments that must follow this argument
13036// If FIELD is invalid, return -1
13037i32 command::atf_comp_NArgs(command::FieldId field, algo::strptr& out_dflt, bool* out_anon) {
13038    i32 retval = 1;
13039    switch (field) {
13040        case command_FieldId_in: { // $comment
13041            *out_anon = false;
13042        } break;
13043        case command_FieldId_comptest: { // $comment
13044            *out_anon = true;
13045        } break;
13046        case command_FieldId_mdbg: { // $comment
13047            *out_anon = false;
13048            retval=0;
13049            out_dflt="Y";
13050        } break;
13051        case command_FieldId_run: { // bool: no argument required but value may be specified as mdbg:Y
13052            *out_anon = false;
13053            retval=0;
13054            out_dflt="Y";
13055        } break;
13056        case command_FieldId_capture: { // bool: no argument required but value may be specified as run:Y
13057            *out_anon = false;
13058            retval=0;
13059            out_dflt="Y";
13060        } break;
13061        case command_FieldId_print: { // bool: no argument required but value may be specified as capture:Y
13062            *out_anon = false;
13063            retval=0;
13064            out_dflt="Y";
13065        } break;
13066        case command_FieldId_printinput: { // bool: no argument required but value may be specified as print:Y
13067            *out_anon = false;
13068            retval=0;
13069            out_dflt="Y";
13070        } break;
13071        case command_FieldId_e: { // bool: no argument required but value may be specified as printinput:Y
13072            *out_anon = false;
13073            retval=0;
13074            out_dflt="Y";
13075        } break;
13076        case command_FieldId_normalize: { // bool: no argument required but value may be specified as e:Y
13077            *out_anon = false;
13078            retval=0;
13079            out_dflt="Y";
13080        } break;
13081        case command_FieldId_covcapture: { // bool: no argument required but value may be specified as normalize:Y
13082            *out_anon = false;
13083            retval=0;
13084            out_dflt="Y";
13085        } break;
13086        case command_FieldId_covcheck: { // bool: no argument required but value may be specified as covcapture:Y
13087            *out_anon = false;
13088            retval=0;
13089            out_dflt="Y";
13090        } break;
13091        case command_FieldId_compdir: { // bool: no argument required but value may be specified as covcheck:Y
13092            *out_anon = false;
13093        } break;
13094        case command_FieldId_cfg: { // bool: no argument required but value may be specified as covcheck:Y
13095            *out_anon = false;
13096        } break;
13097        case command_FieldId_check_untracked: { // bool: no argument required but value may be specified as covcheck:Y
13098            *out_anon = false;
13099            retval=0;
13100            out_dflt="Y";
13101        } break;
13102        case command_FieldId_maxerr: { // bool: no argument required but value may be specified as check_untracked:Y
13103            *out_anon = false;
13104        } break;
13105        case command_FieldId_build: { // bool: no argument required but value may be specified as check_untracked:Y
13106            *out_anon = false;
13107            retval=0;
13108            out_dflt="Y";
13109        } break;
13110        case command_FieldId_ood: { // bool: no argument required but value may be specified as build:Y
13111            *out_anon = false;
13112            retval=0;
13113            out_dflt="Y";
13114        } break;
13115        case command_FieldId_memcheck: { // bool: no argument required but value may be specified as ood:Y
13116            *out_anon = false;
13117            retval=0;
13118            out_dflt="Y";
13119        } break;
13120        case command_FieldId_force: { // bool: no argument required but value may be specified as memcheck:Y
13121            *out_anon = false;
13122            retval=0;
13123            out_dflt="Y";
13124        } break;
13125        case command_FieldId_callgrind: { // bool: no argument required but value may be specified as force:Y
13126            *out_anon = false;
13127            retval=0;
13128            out_dflt="Y";
13129        } break;
13130        case command_FieldId_maxjobs: { // bool: no argument required but value may be specified as callgrind:Y
13131            *out_anon = false;
13132        } break;
13133        case command_FieldId_stream: { // bool: no argument required but value may be specified as callgrind:Y
13134            *out_anon = false;
13135            retval=0;
13136            out_dflt="Y";
13137        } break;
13138        case command_FieldId_i: { // bool: no argument required but value may be specified as stream:Y
13139            *out_anon = false;
13140            retval=0;
13141            out_dflt="Y";
13142        } break;
13143        case command_FieldId_write: { // bool: no argument required but value may be specified as i:Y
13144            *out_anon = false;
13145            retval=0;
13146            out_dflt="Y";
13147        } break;
13148        case command_FieldId_report: { // bool: no argument required but value may be specified as write:Y
13149            *out_anon = false;
13150            retval=0;
13151            out_dflt="Y";
13152        } break;
13153        case command_FieldId_b: { // bool: no argument required but value may be specified as report:Y
13154            *out_anon = false;
13155        } break;
13156        default:
13157        retval=-1; // unrecognized
13158    }
13159    return retval;
13160}
13161
13162// --- command.atf_comp_proc.atf_comp.Start
13163// Start subprocess
13164// If subprocess already running, do nothing. Otherwise, start it
13165int command::atf_comp_Start(command::atf_comp_proc& parent) {
13166    int retval = 0;
13167    if (parent.pid == 0) {
13168        verblog(atf_comp_ToCmdline(parent)); // maybe print command
13169#ifdef WIN32
13170        algo_lib::ResolveExecFname(parent.path);
13171        tempstr cmdline(atf_comp_ToCmdline(parent));
13172        parent.pid = dospawn(Zeroterm(parent.path),Zeroterm(cmdline),parent.timeout,parent.fstdin,parent.fstdout,parent.fstderr);
13173#else
13174        parent.pid = fork();
13175        if (parent.pid == 0) { // child
13176            algo_lib::DieWithParent();
13177            if (parent.timeout > 0) {
13178                alarm(parent.timeout);
13179            }
13180            if (retval==0) retval=algo_lib::ApplyRedirect(parent.fstdin , 0);
13181            if (retval==0) retval=algo_lib::ApplyRedirect(parent.fstdout, 1);
13182            if (retval==0) retval=algo_lib::ApplyRedirect(parent.fstderr, 2);
13183            if (retval==0) retval= atf_comp_Execv(parent);
13184            if (retval != 0) { // if start fails, print error
13185                int err=errno;
13186                prerr("command.atf_comp_execv"
13187                <<Keyval("errno",err)
13188                <<Keyval("errstr",strerror(err))
13189                <<Keyval("comment","Execv failed"));
13190            }
13191            _exit(127); // if failed to start, exit anyway
13192        } else if (parent.pid == -1) {
13193            retval = errno; // failed to fork
13194        }
13195#endif
13196    }
13197    parent.status = parent.pid > 0 ? 0 : -1; // if didn't start, set error status
13198    return retval;
13199}
13200
13201// --- command.atf_comp_proc.atf_comp.StartRead
13202// Start subprocess & Read output
13203algo::Fildes command::atf_comp_StartRead(command::atf_comp_proc& parent, algo_lib::FFildes &read) {
13204    int pipefd[2];
13205    int rc=pipe(pipefd);
13206    (void)rc;
13207    read.fd.value = pipefd[0];
13208    parent.fstdout  << ">&" << pipefd[1];
13209    atf_comp_Start(parent);
13210    (void)close(pipefd[1]);
13211    return read.fd;
13212}
13213
13214// --- command.atf_comp_proc.atf_comp.Kill
13215// Kill subprocess and wait
13216void command::atf_comp_Kill(command::atf_comp_proc& parent) {
13217    if (parent.pid != 0) {
13218        kill(parent.pid,9);
13219        atf_comp_Wait(parent);
13220    }
13221}
13222
13223// --- command.atf_comp_proc.atf_comp.Wait
13224// Wait for subprocess to return
13225void command::atf_comp_Wait(command::atf_comp_proc& parent) {
13226    if (parent.pid > 0) {
13227        int wait_flags = 0;
13228        int wait_status = 0;
13229        int rc = -1;
13230        do {
13231            // really wait for subprocess to exit
13232            rc = waitpid(parent.pid,&wait_status,wait_flags);
13233        } while (rc==-1 && errno==EINTR);
13234        if (rc == parent.pid) {
13235            parent.status = wait_status;
13236            parent.pid = 0;
13237        }
13238    }
13239}
13240
13241// --- command.atf_comp_proc.atf_comp.Exec
13242// Start + Wait
13243// Execute subprocess and return exit code
13244int command::atf_comp_Exec(command::atf_comp_proc& parent) {
13245    atf_comp_Start(parent);
13246    atf_comp_Wait(parent);
13247    return parent.status;
13248}
13249
13250// --- command.atf_comp_proc.atf_comp.ExecX
13251// Start + Wait, throw exception on error
13252// Execute subprocess; throw human-readable exception on error
13253void command::atf_comp_ExecX(command::atf_comp_proc& parent) {
13254    int rc = atf_comp_Exec(parent);
13255    vrfy(rc==0, tempstr() << "algo_lib.exec" << Keyval("cmd",atf_comp_ToCmdline(parent))
13256    << Keyval("comment",algo::DescribeWaitStatus(parent.status)));
13257}
13258
13259// --- command.atf_comp_proc.atf_comp.Execv
13260// Call execv()
13261// Call execv with specified parameters
13262int command::atf_comp_Execv(command::atf_comp_proc& parent) {
13263    int ret = 0;
13264    algo::StringAry args;
13265    atf_comp_ToArgv(parent, args);
13266    char **argv = (char**)alloca((ary_N(args)+1)*sizeof(*argv));
13267    ind_beg(algo::StringAry_ary_curs,arg,args) {
13268        argv[ind_curs(arg).index] = Zeroterm(arg);
13269    }ind_end;
13270    argv[ary_N(args)] = NULL;
13271    // if parent.path is relative, search for it in PATH
13272    algo_lib::ResolveExecFname(parent.path);
13273    ret = execv(Zeroterm(parent.path),argv);
13274    return ret;
13275}
13276
13277// --- command.atf_comp_proc.atf_comp.ToCmdline
13278algo::tempstr command::atf_comp_ToCmdline(command::atf_comp_proc& parent) {
13279    algo::tempstr retval;
13280    retval << parent.path << " ";
13281    command::atf_comp_PrintArgv(parent.cmd,retval);
13282    if (ch_N(parent.fstdin)) {
13283        retval << " " << parent.fstdin;
13284    }
13285    if (ch_N(parent.fstdout)) {
13286        retval << " " << parent.fstdout;
13287    }
13288    if (ch_N(parent.fstderr)) {
13289        retval << " 2" << parent.fstderr;
13290    }
13291    return retval;
13292}
13293
13294// --- command.atf_comp_proc.atf_comp.ToArgv
13295// Form array from the command line
13296void command::atf_comp_ToArgv(command::atf_comp_proc& parent, algo::StringAry& args) {
13297    ary_RemoveAll(args);
13298    ary_Alloc(args) << parent.path;
13299
13300    if (parent.cmd.in != "data") {
13301        cstring *arg = &ary_Alloc(args);
13302        *arg << "-in:";
13303        cstring_Print(parent.cmd.in, *arg);
13304    }
13305
13306    if (parent.cmd.comptest.expr != "%") {
13307        cstring *arg = &ary_Alloc(args);
13308        *arg << "-comptest:";
13309        command::comptest_Print(parent.cmd, *arg);
13310    }
13311
13312    if (parent.cmd.mdbg != false) {
13313        cstring *arg = &ary_Alloc(args);
13314        *arg << "-mdbg:";
13315        bool_Print(parent.cmd.mdbg, *arg);
13316    }
13317
13318    if (parent.cmd.run != true) {
13319        cstring *arg = &ary_Alloc(args);
13320        *arg << "-run:";
13321        bool_Print(parent.cmd.run, *arg);
13322    }
13323
13324    if (parent.cmd.capture != false) {
13325        cstring *arg = &ary_Alloc(args);
13326        *arg << "-capture:";
13327        bool_Print(parent.cmd.capture, *arg);
13328    }
13329
13330    if (parent.cmd.print != false) {
13331        cstring *arg = &ary_Alloc(args);
13332        *arg << "-print:";
13333        bool_Print(parent.cmd.print, *arg);
13334    }
13335
13336    if (parent.cmd.printinput != false) {
13337        cstring *arg = &ary_Alloc(args);
13338        *arg << "-printinput:";
13339        bool_Print(parent.cmd.printinput, *arg);
13340    }
13341
13342    if (parent.cmd.e != false) {
13343        cstring *arg = &ary_Alloc(args);
13344        *arg << "-e:";
13345        bool_Print(parent.cmd.e, *arg);
13346    }
13347
13348    if (parent.cmd.normalize != false) {
13349        cstring *arg = &ary_Alloc(args);
13350        *arg << "-normalize:";
13351        bool_Print(parent.cmd.normalize, *arg);
13352    }
13353
13354    if (parent.cmd.covcapture != false) {
13355        cstring *arg = &ary_Alloc(args);
13356        *arg << "-covcapture:";
13357        bool_Print(parent.cmd.covcapture, *arg);
13358    }
13359
13360    if (parent.cmd.covcheck != false) {
13361        cstring *arg = &ary_Alloc(args);
13362        *arg << "-covcheck:";
13363        bool_Print(parent.cmd.covcheck, *arg);
13364    }
13365
13366    if (parent.cmd.compdir != "") {
13367        cstring *arg = &ary_Alloc(args);
13368        *arg << "-compdir:";
13369        cstring_Print(parent.cmd.compdir, *arg);
13370    }
13371
13372    if (parent.cmd.cfg != "release") {
13373        cstring *arg = &ary_Alloc(args);
13374        *arg << "-cfg:";
13375        Smallstr50_Print(parent.cmd.cfg, *arg);
13376    }
13377
13378    if (parent.cmd.check_untracked != true) {
13379        cstring *arg = &ary_Alloc(args);
13380        *arg << "-check_untracked:";
13381        bool_Print(parent.cmd.check_untracked, *arg);
13382    }
13383
13384    if (parent.cmd.maxerr != 1) {
13385        cstring *arg = &ary_Alloc(args);
13386        *arg << "-maxerr:";
13387        i32_Print(parent.cmd.maxerr, *arg);
13388    }
13389
13390    if (parent.cmd.build != false) {
13391        cstring *arg = &ary_Alloc(args);
13392        *arg << "-build:";
13393        bool_Print(parent.cmd.build, *arg);
13394    }
13395
13396    if (parent.cmd.ood != false) {
13397        cstring *arg = &ary_Alloc(args);
13398        *arg << "-ood:";
13399        bool_Print(parent.cmd.ood, *arg);
13400    }
13401
13402    if (parent.cmd.memcheck != false) {
13403        cstring *arg = &ary_Alloc(args);
13404        *arg << "-memcheck:";
13405        bool_Print(parent.cmd.memcheck, *arg);
13406    }
13407
13408    if (parent.cmd.force != false) {
13409        cstring *arg = &ary_Alloc(args);
13410        *arg << "-force:";
13411        bool_Print(parent.cmd.force, *arg);
13412    }
13413
13414    if (parent.cmd.callgrind != false) {
13415        cstring *arg = &ary_Alloc(args);
13416        *arg << "-callgrind:";
13417        bool_Print(parent.cmd.callgrind, *arg);
13418    }
13419
13420    if (parent.cmd.maxjobs != 0) {
13421        cstring *arg = &ary_Alloc(args);
13422        *arg << "-maxjobs:";
13423        i32_Print(parent.cmd.maxjobs, *arg);
13424    }
13425
13426    if (parent.cmd.stream != false) {
13427        cstring *arg = &ary_Alloc(args);
13428        *arg << "-stream:";
13429        bool_Print(parent.cmd.stream, *arg);
13430    }
13431
13432    if (parent.cmd.i != false) {
13433        cstring *arg = &ary_Alloc(args);
13434        *arg << "-i:";
13435        bool_Print(parent.cmd.i, *arg);
13436    }
13437
13438    if (parent.cmd.write != true) {
13439        cstring *arg = &ary_Alloc(args);
13440        *arg << "-write:";
13441        bool_Print(parent.cmd.write, *arg);
13442    }
13443
13444    if (parent.cmd.report != false) {
13445        cstring *arg = &ary_Alloc(args);
13446        *arg << "-report:";
13447        bool_Print(parent.cmd.report, *arg);
13448    }
13449
13450    if (parent.cmd.b != "") {
13451        cstring *arg = &ary_Alloc(args);
13452        *arg << "-b:";
13453        cstring_Print(parent.cmd.b, *arg);
13454    }
13455    for (int i=1; i < algo_lib::_db.cmdline.verbose; ++i) {
13456        ary_Alloc(args) << "-verbose";
13457    }
13458}
13459
13460// --- command.atf_comp_proc..Uninit
13461void command::atf_comp_proc_Uninit(command::atf_comp_proc& parent) {
13462    command::atf_comp_proc &row = parent; (void)row;
13463
13464    // command.atf_comp_proc.atf_comp.Uninit (Exec)  //
13465    atf_comp_Kill(parent); // kill child, ensure forward progress
13466}
13467
13468// --- command.atf_cov.exclude.Print
13469// Print back to string
13470void command::exclude_Print(command::atf_cov& parent, algo::cstring &out) {
13471    Regx_Print(parent.exclude, out);
13472}
13473
13474// --- command.atf_cov.exclude.ReadStrptrMaybe
13475// Read Regx from string
13476// Convert string to field. Return success value
13477bool command::exclude_ReadStrptrMaybe(command::atf_cov& parent, algo::strptr in) {
13478    bool retval = true;
13479    Regx_ReadSql(parent.exclude, in, true);
13480    return retval;
13481}
13482
13483// --- command.atf_cov..ReadFieldMaybe
13484bool command::atf_cov_ReadFieldMaybe(command::atf_cov& parent, algo::strptr field, algo::strptr strval) {
13485    bool retval = true;
13486    command::FieldId field_id;
13487    (void)value_SetStrptrMaybe(field_id,field);
13488    switch(field_id) {
13489        case command_FieldId_in: {
13490            retval = algo::cstring_ReadStrptrMaybe(parent.in, strval);
13491            break;
13492        }
13493        case command_FieldId_covdir: {
13494            retval = algo::cstring_ReadStrptrMaybe(parent.covdir, strval);
13495            break;
13496        }
13497        case command_FieldId_logfile: {
13498            retval = algo::cstring_ReadStrptrMaybe(parent.logfile, strval);
13499            break;
13500        }
13501        case command_FieldId_runcmd: {
13502            retval = algo::cstring_ReadStrptrMaybe(parent.runcmd, strval);
13503            break;
13504        }
13505        case command_FieldId_exclude: {
13506            retval = exclude_ReadStrptrMaybe(parent, strval);
13507            break;
13508        }
13509        case command_FieldId_mergepath: {
13510            retval = algo::cstring_ReadStrptrMaybe(parent.mergepath, strval);
13511            break;
13512        }
13513        case command_FieldId_gcov: {
13514            retval = bool_ReadStrptrMaybe(parent.gcov, strval);
13515            break;
13516        }
13517        case command_FieldId_ssim: {
13518            retval = bool_ReadStrptrMaybe(parent.ssim, strval);
13519            break;
13520        }
13521        case command_FieldId_report: {
13522            retval = bool_ReadStrptrMaybe(parent.report, strval);
13523            break;
13524        }
13525        case command_FieldId_capture: {
13526            retval = bool_ReadStrptrMaybe(parent.capture, strval);
13527            break;
13528        }
13529        case command_FieldId_xmlpretty: {
13530            retval = bool_ReadStrptrMaybe(parent.xmlpretty, strval);
13531            break;
13532        }
13533        case command_FieldId_summary: {
13534            retval = bool_ReadStrptrMaybe(parent.summary, strval);
13535            break;
13536        }
13537        case command_FieldId_check: {
13538            retval = bool_ReadStrptrMaybe(parent.check, strval);
13539            break;
13540        }
13541        default: break;
13542    }
13543    if (!retval) {
13544        algo_lib::AppendErrtext("attr",field);
13545    }
13546    return retval;
13547}
13548
13549// --- command.atf_cov..ReadTupleMaybe
13550// Read fields of command::atf_cov from attributes of ascii tuple TUPLE
13551bool command::atf_cov_ReadTupleMaybe(command::atf_cov &parent, algo::Tuple &tuple) {
13552    bool retval = true;
13553    ind_beg(algo::Tuple_attrs_curs,attr,tuple) {
13554        retval = atf_cov_ReadFieldMaybe(parent, attr.name, attr.value);
13555        if (!retval) {
13556            break;
13557        }
13558    }ind_end;
13559    return retval;
13560}
13561
13562// --- command.atf_cov..Init
13563// Set all fields to initial values.
13564void command::atf_cov_Init(command::atf_cov& parent) {
13565    parent.in = algo::strptr("data");
13566    parent.covdir = algo::strptr("temp/covdata");
13567    parent.logfile = algo::strptr("");
13568    parent.runcmd = algo::strptr("");
13569    Regx_ReadSql(parent.exclude, "(extern|include/gen|cpp/gen)/%", true);
13570    parent.mergepath = algo::strptr("");
13571    parent.gcov = bool(false);
13572    parent.ssim = bool(false);
13573    parent.report = bool(false);
13574    parent.capture = bool(false);
13575    parent.xmlpretty = bool(false);
13576    parent.summary = bool(true);
13577    parent.check = bool(false);
13578}
13579
13580// --- command.atf_cov..ToCmdline
13581// Convenience function that returns a full command line
13582// Assume command is in a directory called bin
13583tempstr command::atf_cov_ToCmdline(command::atf_cov& row) {
13584    tempstr ret;
13585    ret << "bin/atf_cov ";
13586    atf_cov_PrintArgv(row, ret);
13587    // inherit less intense verbose, debug options
13588    for (int i = 1; i < algo_lib::_db.cmdline.verbose; i++) {
13589        ret << " -verbose";
13590    }
13591    for (int i = 1; i < algo_lib::_db.cmdline.debug; i++) {
13592        ret << " -debug";
13593    }
13594    return ret;
13595}
13596
13597// --- command.atf_cov..PrintArgv
13598// print string representation of ROW to string STR
13599// cfmt:command.atf_cov.Argv  printfmt:Tuple
13600void command::atf_cov_PrintArgv(command::atf_cov& row, algo::cstring& str) {
13601    algo::tempstr temp;
13602    (void)temp;
13603    (void)str;
13604    if (!(row.in == "data")) {
13605        ch_RemoveAll(temp);
13606        cstring_Print(row.in, temp);
13607        str << " -in:";
13608        strptr_PrintBash(temp,str);
13609    }
13610    if (!(row.covdir == "temp/covdata")) {
13611        ch_RemoveAll(temp);
13612        cstring_Print(row.covdir, temp);
13613        str << " -covdir:";
13614        strptr_PrintBash(temp,str);
13615    }
13616    if (!(row.logfile == "")) {
13617        ch_RemoveAll(temp);
13618        cstring_Print(row.logfile, temp);
13619        str << " -logfile:";
13620        strptr_PrintBash(temp,str);
13621    }
13622    if (!(row.runcmd == "")) {
13623        ch_RemoveAll(temp);
13624        cstring_Print(row.runcmd, temp);
13625        str << " -runcmd:";
13626        strptr_PrintBash(temp,str);
13627    }
13628    if (!(row.exclude.expr == "(extern|include/gen|cpp/gen)/%")) {
13629        ch_RemoveAll(temp);
13630        command::exclude_Print(const_cast<command::atf_cov&>(row), temp);
13631        str << " -exclude:";
13632        strptr_PrintBash(temp,str);
13633    }
13634    if (!(row.mergepath == "")) {
13635        ch_RemoveAll(temp);
13636        cstring_Print(row.mergepath, temp);
13637        str << " -mergepath:";
13638        strptr_PrintBash(temp,str);
13639    }
13640    if (!(row.gcov == false)) {
13641        ch_RemoveAll(temp);
13642        bool_Print(row.gcov, temp);
13643        str << " -gcov:";
13644        strptr_PrintBash(temp,str);
13645    }
13646    if (!(row.ssim == false)) {
13647        ch_RemoveAll(temp);
13648        bool_Print(row.ssim, temp);
13649        str << " -ssim:";
13650        strptr_PrintBash(temp,str);
13651    }
13652    if (!(row.report == false)) {
13653        ch_RemoveAll(temp);
13654        bool_Print(row.report, temp);
13655        str << " -report:";
13656        strptr_PrintBash(temp,str);
13657    }
13658    if (!(row.capture == false)) {
13659        ch_RemoveAll(temp);
13660        bool_Print(row.capture, temp);
13661        str << " -capture:";
13662        strptr_PrintBash(temp,str);
13663    }
13664    if (!(row.xmlpretty == false)) {
13665        ch_RemoveAll(temp);
13666        bool_Print(row.xmlpretty, temp);
13667        str << " -xmlpretty:";
13668        strptr_PrintBash(temp,str);
13669    }
13670    if (!(row.summary == true)) {
13671        ch_RemoveAll(temp);
13672        bool_Print(row.summary, temp);
13673        str << " -summary:";
13674        strptr_PrintBash(temp,str);
13675    }
13676    if (!(row.check == false)) {
13677        ch_RemoveAll(temp);
13678        bool_Print(row.check, temp);
13679        str << " -check:";
13680        strptr_PrintBash(temp,str);
13681    }
13682}
13683
13684// --- command.atf_cov..NArgs
13685// Used with command lines
13686// Return # of command-line arguments that must follow this argument
13687// If FIELD is invalid, return -1
13688i32 command::atf_cov_NArgs(command::FieldId field, algo::strptr& out_dflt, bool* out_anon) {
13689    i32 retval = 1;
13690    switch (field) {
13691        case command_FieldId_in: { // $comment
13692            *out_anon = false;
13693        } break;
13694        case command_FieldId_covdir: { // $comment
13695            *out_anon = false;
13696        } break;
13697        case command_FieldId_logfile: { // $comment
13698            *out_anon = false;
13699        } break;
13700        case command_FieldId_runcmd: { // $comment
13701            *out_anon = false;
13702        } break;
13703        case command_FieldId_exclude: { // $comment
13704            *out_anon = false;
13705        } break;
13706        case command_FieldId_mergepath: { // $comment
13707            *out_anon = false;
13708        } break;
13709        case command_FieldId_gcov: { // $comment
13710            *out_anon = false;
13711            retval=0;
13712            out_dflt="Y";
13713        } break;
13714        case command_FieldId_ssim: { // bool: no argument required but value may be specified as gcov:Y
13715            *out_anon = false;
13716            retval=0;
13717            out_dflt="Y";
13718        } break;
13719        case command_FieldId_report: { // bool: no argument required but value may be specified as ssim:Y
13720            *out_anon = false;
13721            retval=0;
13722            out_dflt="Y";
13723        } break;
13724        case command_FieldId_capture: { // bool: no argument required but value may be specified as report:Y
13725            *out_anon = false;
13726            retval=0;
13727            out_dflt="Y";
13728        } break;
13729        case command_FieldId_xmlpretty: { // bool: no argument required but value may be specified as capture:Y
13730            *out_anon = false;
13731            retval=0;
13732            out_dflt="Y";
13733        } break;
13734        case command_FieldId_summary: { // bool: no argument required but value may be specified as xmlpretty:Y
13735            *out_anon = false;
13736            retval=0;
13737            out_dflt="Y";
13738        } break;
13739        case command_FieldId_check: { // bool: no argument required but value may be specified as summary:Y
13740            *out_anon = false;
13741            retval=0;
13742            out_dflt="Y";
13743        } break;
13744        default:
13745        retval=-1; // unrecognized
13746    }
13747    return retval;
13748}
13749
13750// --- command.atf_cov_proc.atf_cov.Start
13751// Start subprocess
13752// If subprocess already running, do nothing. Otherwise, start it
13753int command::atf_cov_Start(command::atf_cov_proc& parent) {
13754    int retval = 0;
13755    if (parent.pid == 0) {
13756        verblog(atf_cov_ToCmdline(parent)); // maybe print command
13757#ifdef WIN32
13758        algo_lib::ResolveExecFname(parent.path);
13759        tempstr cmdline(atf_cov_ToCmdline(parent));
13760        parent.pid = dospawn(Zeroterm(parent.path),Zeroterm(cmdline),parent.timeout,parent.fstdin,parent.fstdout,parent.fstderr);
13761#else
13762        parent.pid = fork();
13763        if (parent.pid == 0) { // child
13764            algo_lib::DieWithParent();
13765            if (parent.timeout > 0) {
13766                alarm(parent.timeout);
13767            }
13768            if (retval==0) retval=algo_lib::ApplyRedirect(parent.fstdin , 0);
13769            if (retval==0) retval=algo_lib::ApplyRedirect(parent.fstdout, 1);
13770            if (retval==0) retval=algo_lib::ApplyRedirect(parent.fstderr, 2);
13771            if (retval==0) retval= atf_cov_Execv(parent);
13772            if (retval != 0) { // if start fails, print error
13773                int err=errno;
13774                prerr("command.atf_cov_execv"
13775                <<Keyval("errno",err)
13776                <<Keyval("errstr",strerror(err))
13777                <<Keyval("comment","Execv failed"));
13778            }
13779            _exit(127); // if failed to start, exit anyway
13780        } else if (parent.pid == -1) {
13781            retval = errno; // failed to fork
13782        }
13783#endif
13784    }
13785    parent.status = parent.pid > 0 ? 0 : -1; // if didn't start, set error status
13786    return retval;
13787}
13788
13789// --- command.atf_cov_proc.atf_cov.StartRead
13790// Start subprocess & Read output
13791algo::Fildes command::atf_cov_StartRead(command::atf_cov_proc& parent, algo_lib::FFildes &read) {
13792    int pipefd[2];
13793    int rc=pipe(pipefd);
13794    (void)rc;
13795    read.fd.value = pipefd[0];
13796    parent.fstdout  << ">&" << pipefd[1];
13797    atf_cov_Start(parent);
13798    (void)close(pipefd[1]);
13799    return read.fd;
13800}
13801
13802// --- command.atf_cov_proc.atf_cov.Kill
13803// Kill subprocess and wait
13804void command::atf_cov_Kill(command::atf_cov_proc& parent) {
13805    if (parent.pid != 0) {
13806        kill(parent.pid,9);
13807        atf_cov_Wait(parent);
13808    }
13809}
13810
13811// --- command.atf_cov_proc.atf_cov.Wait
13812// Wait for subprocess to return
13813void command::atf_cov_Wait(command::atf_cov_proc& parent) {
13814    if (parent.pid > 0) {
13815        int wait_flags = 0;
13816        int wait_status = 0;
13817        int rc = -1;
13818        do {
13819            // really wait for subprocess to exit
13820            rc = waitpid(parent.pid,&wait_status,wait_flags);
13821        } while (rc==-1 && errno==EINTR);
13822        if (rc == parent.pid) {
13823            parent.status = wait_status;
13824            parent.pid = 0;
13825        }
13826    }
13827}
13828
13829// --- command.atf_cov_proc.atf_cov.Exec
13830// Start + Wait
13831// Execute subprocess and return exit code
13832int command::atf_cov_Exec(command::atf_cov_proc& parent) {
13833    atf_cov_Start(parent);
13834    atf_cov_Wait(parent);
13835    return parent.status;
13836}
13837
13838// --- command.atf_cov_proc.atf_cov.ExecX
13839// Start + Wait, throw exception on error
13840// Execute subprocess; throw human-readable exception on error
13841void command::atf_cov_ExecX(command::atf_cov_proc& parent) {
13842    int rc = atf_cov_Exec(parent);
13843    vrfy(rc==0, tempstr() << "algo_lib.exec" << Keyval("cmd",atf_cov_ToCmdline(parent))
13844    << Keyval("comment",algo::DescribeWaitStatus(parent.status)));
13845}
13846
13847// --- command.atf_cov_proc.atf_cov.Execv
13848// Call execv()
13849// Call execv with specified parameters
13850int command::atf_cov_Execv(command::atf_cov_proc& parent) {
13851    int ret = 0;
13852    algo::StringAry args;
13853    atf_cov_ToArgv(parent, args);
13854    char **argv = (char**)alloca((ary_N(args)+1)*sizeof(*argv));
13855    ind_beg(algo::StringAry_ary_curs,arg,args) {
13856        argv[ind_curs(arg).index] = Zeroterm(arg);
13857    }ind_end;
13858    argv[ary_N(args)] = NULL;
13859    // if parent.path is relative, search for it in PATH
13860    algo_lib::ResolveExecFname(parent.path);
13861    ret = execv(Zeroterm(parent.path),argv);
13862    return ret;
13863}
13864
13865// --- command.atf_cov_proc.atf_cov.ToCmdline
13866algo::tempstr command::atf_cov_ToCmdline(command::atf_cov_proc& parent) {
13867    algo::tempstr retval;
13868    retval << parent.path << " ";
13869    command::atf_cov_PrintArgv(parent.cmd,retval);
13870    if (ch_N(parent.fstdin)) {
13871        retval << " " << parent.fstdin;
13872    }
13873    if (ch_N(parent.fstdout)) {
13874        retval << " " << parent.fstdout;
13875    }
13876    if (ch_N(parent.fstderr)) {
13877        retval << " 2" << parent.fstderr;
13878    }
13879    return retval;
13880}
13881
13882// --- command.atf_cov_proc.atf_cov.ToArgv
13883// Form array from the command line
13884void command::atf_cov_ToArgv(command::atf_cov_proc& parent, algo::StringAry& args) {
13885    ary_RemoveAll(args);
13886    ary_Alloc(args) << parent.path;
13887
13888    if (parent.cmd.in != "data") {
13889        cstring *arg = &ary_Alloc(args);
13890        *arg << "-in:";
13891        cstring_Print(parent.cmd.in, *arg);
13892    }
13893
13894    if (parent.cmd.covdir != "temp/covdata") {
13895        cstring *arg = &ary_Alloc(args);
13896        *arg << "-covdir:";
13897        cstring_Print(parent.cmd.covdir, *arg);
13898    }
13899
13900    if (parent.cmd.logfile != "") {
13901        cstring *arg = &ary_Alloc(args);
13902        *arg << "-logfile:";
13903        cstring_Print(parent.cmd.logfile, *arg);
13904    }
13905
13906    if (parent.cmd.runcmd != "") {
13907        cstring *arg = &ary_Alloc(args);
13908        *arg << "-runcmd:";
13909        cstring_Print(parent.cmd.runcmd, *arg);
13910    }
13911
13912    if (parent.cmd.exclude.expr != "(extern|include/gen|cpp/gen)/%") {
13913        cstring *arg = &ary_Alloc(args);
13914        *arg << "-exclude:";
13915        command::exclude_Print(parent.cmd, *arg);
13916    }
13917
13918    if (parent.cmd.mergepath != "") {
13919        cstring *arg = &ary_Alloc(args);
13920        *arg << "-mergepath:";
13921        cstring_Print(parent.cmd.mergepath, *arg);
13922    }
13923
13924    if (parent.cmd.gcov != false) {
13925        cstring *arg = &ary_Alloc(args);
13926        *arg << "-gcov:";
13927        bool_Print(parent.cmd.gcov, *arg);
13928    }
13929
13930    if (parent.cmd.ssim != false) {
13931        cstring *arg = &ary_Alloc(args);
13932        *arg << "-ssim:";
13933        bool_Print(parent.cmd.ssim, *arg);
13934    }
13935
13936    if (parent.cmd.report != false) {
13937        cstring *arg = &ary_Alloc(args);
13938        *arg << "-report:";
13939        bool_Print(parent.cmd.report, *arg);
13940    }
13941
13942    if (parent.cmd.capture != false) {
13943        cstring *arg = &ary_Alloc(args);
13944        *arg << "-capture:";
13945        bool_Print(parent.cmd.capture, *arg);
13946    }
13947
13948    if (parent.cmd.xmlpretty != false) {
13949        cstring *arg = &ary_Alloc(args);
13950        *arg << "-xmlpretty:";
13951        bool_Print(parent.cmd.xmlpretty, *arg);
13952    }
13953
13954    if (parent.cmd.summary != true) {
13955        cstring *arg = &ary_Alloc(args);
13956        *arg << "-summary:";
13957        bool_Print(parent.cmd.summary, *arg);
13958    }
13959
13960    if (parent.cmd.check != false) {
13961        cstring *arg = &ary_Alloc(args);
13962        *arg << "-check:";
13963        bool_Print(parent.cmd.check, *arg);
13964    }
13965    for (int i=1; i < algo_lib::_db.cmdline.verbose; ++i) {
13966        ary_Alloc(args) << "-verbose";
13967    }
13968}
13969
13970// --- command.atf_cov_proc..Uninit
13971void command::atf_cov_proc_Uninit(command::atf_cov_proc& parent) {
13972    command::atf_cov_proc &row = parent; (void)row;
13973
13974    // command.atf_cov_proc.atf_cov.Uninit (Exec)  //
13975    atf_cov_Kill(parent); // kill child, ensure forward progress
13976}
13977
13978// --- command.atf_fuzz.fuzzstrat.Print
13979// Print back to string
13980void command::fuzzstrat_Print(command::atf_fuzz& parent, algo::cstring &out) {
13981    Regx_Print(parent.fuzzstrat, out);
13982}
13983
13984// --- command.atf_fuzz.fuzzstrat.ReadStrptrMaybe
13985// Read Regx from string
13986// Convert string to field. Return success value
13987bool command::fuzzstrat_ReadStrptrMaybe(command::atf_fuzz& parent, algo::strptr in) {
13988    bool retval = true;
13989    Regx_ReadSql(parent.fuzzstrat, in, true);
13990    return retval;
13991}
13992
13993// --- command.atf_fuzz..ReadFieldMaybe
13994bool command::atf_fuzz_ReadFieldMaybe(command::atf_fuzz& parent, algo::strptr field, algo::strptr strval) {
13995    bool retval = true;
13996    command::FieldId field_id;
13997    (void)value_SetStrptrMaybe(field_id,field);
13998    switch(field_id) {
13999        case command_FieldId_reprofile: {
14000            retval = algo::cstring_ReadStrptrMaybe(parent.reprofile, strval);
14001            break;
14002        }
14003        case command_FieldId_target: {
14004            retval = algo::Smallstr16_ReadStrptrMaybe(parent.target, strval);
14005            break;
14006        }
14007        case command_FieldId_args: {
14008            retval = algo::cstring_ReadStrptrMaybe(parent.args, strval);
14009            break;
14010        }
14011        case command_FieldId_inputfile: {
14012            retval = algo::cstring_ReadStrptrMaybe(parent.inputfile, strval);
14013            break;
14014        }
14015        case command_FieldId_fuzzstrat: {
14016            retval = fuzzstrat_ReadStrptrMaybe(parent, strval);
14017            break;
14018        }
14019        case command_FieldId_in: {
14020            retval = algo::cstring_ReadStrptrMaybe(parent.in, strval);
14021            break;
14022        }
14023        case command_FieldId_seed: {
14024            retval = i32_ReadStrptrMaybe(parent.seed, strval);
14025            break;
14026        }
14027        case command_FieldId_testprob: {
14028            retval = double_ReadStrptrMaybe(parent.testprob, strval);
14029            break;
14030        }
14031        default: break;
14032    }
14033    if (!retval) {
14034        algo_lib::AppendErrtext("attr",field);
14035    }
14036    return retval;
14037}
14038
14039// --- command.atf_fuzz..ReadTupleMaybe
14040// Read fields of command::atf_fuzz from attributes of ascii tuple TUPLE
14041bool command::atf_fuzz_ReadTupleMaybe(command::atf_fuzz &parent, algo::Tuple &tuple) {
14042    bool retval = true;
14043    int anon_idx = 0;
14044    ind_beg(algo::Tuple_attrs_curs,attr,tuple) {
14045        if (ch_N(attr.name) == 0) {
14046            attr.name = atf_fuzz_GetAnon(parent, anon_idx++);
14047        }
14048        retval = atf_fuzz_ReadFieldMaybe(parent, attr.name, attr.value);
14049        if (!retval) {
14050            break;
14051        }
14052    }ind_end;
14053    return retval;
14054}
14055
14056// --- command.atf_fuzz..Init
14057// Set all fields to initial values.
14058void command::atf_fuzz_Init(command::atf_fuzz& parent) {
14059    parent.reprofile = algo::strptr("temp/atf_fuzz.repro");
14060    parent.target = algo::strptr("");
14061    parent.args = algo::strptr("");
14062    parent.inputfile = algo::strptr("");
14063    Regx_ReadSql(parent.fuzzstrat, "%", true);
14064    parent.in = algo::strptr("data");
14065    parent.seed = i32(0);
14066    parent.testprob = double(1);
14067}
14068
14069// --- command.atf_fuzz..ToCmdline
14070// Convenience function that returns a full command line
14071// Assume command is in a directory called bin
14072tempstr command::atf_fuzz_ToCmdline(command::atf_fuzz& row) {
14073    tempstr ret;
14074    ret << "bin/atf_fuzz ";
14075    atf_fuzz_PrintArgv(row, ret);
14076    // inherit less intense verbose, debug options
14077    for (int i = 1; i < algo_lib::_db.cmdline.verbose; i++) {
14078        ret << " -verbose";
14079    }
14080    for (int i = 1; i < algo_lib::_db.cmdline.debug; i++) {
14081        ret << " -debug";
14082    }
14083    return ret;
14084}
14085
14086// --- command.atf_fuzz..PrintArgv
14087// print string representation of ROW to string STR
14088// cfmt:command.atf_fuzz.Argv  printfmt:Tuple
14089void command::atf_fuzz_PrintArgv(command::atf_fuzz& row, algo::cstring& str) {
14090    algo::tempstr temp;
14091    (void)temp;
14092    (void)str;
14093    if (!(row.reprofile == "temp/atf_fuzz.repro")) {
14094        ch_RemoveAll(temp);
14095        cstring_Print(row.reprofile, temp);
14096        str << " -reprofile:";
14097        strptr_PrintBash(temp,str);
14098    }
14099    ch_RemoveAll(temp);
14100    Smallstr16_Print(row.target, temp);
14101    str << " -target:";
14102    strptr_PrintBash(temp,str);
14103    ch_RemoveAll(temp);
14104    cstring_Print(row.args, temp);
14105    str << " -args:";
14106    strptr_PrintBash(temp,str);
14107    if (!(row.inputfile == "")) {
14108        ch_RemoveAll(temp);
14109        cstring_Print(row.inputfile, temp);
14110        str << " -inputfile:";
14111        strptr_PrintBash(temp,str);
14112    }
14113    if (!(row.fuzzstrat.expr == "%")) {
14114        ch_RemoveAll(temp);
14115        command::fuzzstrat_Print(const_cast<command::atf_fuzz&>(row), temp);
14116        str << " -fuzzstrat:";
14117        strptr_PrintBash(temp,str);
14118    }
14119    if (!(row.in == "data")) {
14120        ch_RemoveAll(temp);
14121        cstring_Print(row.in, temp);
14122        str << " -in:";
14123        strptr_PrintBash(temp,str);
14124    }
14125    if (!(row.seed == 0)) {
14126        ch_RemoveAll(temp);
14127        i32_Print(row.seed, temp);
14128        str << " -seed:";
14129        strptr_PrintBash(temp,str);
14130    }
14131    if (!(row.testprob == 1)) {
14132        ch_RemoveAll(temp);
14133        double_Print(row.testprob, temp);
14134        str << " -testprob:";
14135        strptr_PrintBash(temp,str);
14136    }
14137}
14138
14139// --- command.atf_fuzz..GetAnon
14140algo::strptr command::atf_fuzz_GetAnon(command::atf_fuzz &parent, i32 idx) {
14141    (void)parent;//only to avoid -Wunused-parameter
14142    switch(idx) {
14143        case(0): return strptr("target", 6);
14144        case(1): return strptr("args", 4);
14145        default: return algo::strptr();
14146    }
14147}
14148
14149// --- command.atf_fuzz..NArgs
14150// Used with command lines
14151// Return # of command-line arguments that must follow this argument
14152// If FIELD is invalid, return -1
14153i32 command::atf_fuzz_NArgs(command::FieldId field, algo::strptr& out_dflt, bool* out_anon) {
14154    i32 retval = 1;
14155    switch (field) {
14156        case command_FieldId_reprofile: { // $comment
14157            *out_anon = false;
14158        } break;
14159        case command_FieldId_target: { // $comment
14160            *out_anon = true;
14161        } break;
14162        case command_FieldId_args: { // $comment
14163            *out_anon = true;
14164        } break;
14165        case command_FieldId_inputfile: { // $comment
14166            *out_anon = false;
14167        } break;
14168        case command_FieldId_fuzzstrat: { // $comment
14169            *out_anon = false;
14170        } break;
14171        case command_FieldId_in: { // $comment
14172            *out_anon = false;
14173        } break;
14174        case command_FieldId_seed: { // $comment
14175            *out_anon = false;
14176        } break;
14177        case command_FieldId_testprob: { // $comment
14178            *out_anon = false;
14179        } break;
14180        default:
14181        retval=-1; // unrecognized
14182    }
14183    (void)out_dflt;//only to avoid -Wunused-parameter
14184    return retval;
14185}
14186
14187// --- command.atf_fuzz_proc.atf_fuzz.Start
14188// Start subprocess
14189// If subprocess already running, do nothing. Otherwise, start it
14190int command::atf_fuzz_Start(command::atf_fuzz_proc& parent) {
14191    int retval = 0;
14192    if (parent.pid == 0) {
14193        verblog(atf_fuzz_ToCmdline(parent)); // maybe print command
14194#ifdef WIN32
14195        algo_lib::ResolveExecFname(parent.path);
14196        tempstr cmdline(atf_fuzz_ToCmdline(parent));
14197        parent.pid = dospawn(Zeroterm(parent.path),Zeroterm(cmdline),parent.timeout,parent.fstdin,parent.fstdout,parent.fstderr);
14198#else
14199        parent.pid = fork();
14200        if (parent.pid == 0) { // child
14201            algo_lib::DieWithParent();
14202            if (parent.timeout > 0) {
14203                alarm(parent.timeout);
14204            }
14205            if (retval==0) retval=algo_lib::ApplyRedirect(parent.fstdin , 0);
14206            if (retval==0) retval=algo_lib::ApplyRedirect(parent.fstdout, 1);
14207            if (retval==0) retval=algo_lib::ApplyRedirect(parent.fstderr, 2);
14208            if (retval==0) retval= atf_fuzz_Execv(parent);
14209            if (retval != 0) { // if start fails, print error
14210                int err=errno;
14211                prerr("command.atf_fuzz_execv"
14212                <<Keyval("errno",err)
14213                <<Keyval("errstr",strerror(err))
14214                <<Keyval("comment","Execv failed"));
14215            }
14216            _exit(127); // if failed to start, exit anyway
14217        } else if (parent.pid == -1) {
14218            retval = errno; // failed to fork
14219        }
14220#endif
14221    }
14222    parent.status = parent.pid > 0 ? 0 : -1; // if didn't start, set error status
14223    return retval;
14224}
14225
14226// --- command.atf_fuzz_proc.atf_fuzz.StartRead
14227// Start subprocess & Read output
14228algo::Fildes command::atf_fuzz_StartRead(command::atf_fuzz_proc& parent, algo_lib::FFildes &read) {
14229    int pipefd[2];
14230    int rc=pipe(pipefd);
14231    (void)rc;
14232    read.fd.value = pipefd[0];
14233    parent.fstdout  << ">&" << pipefd[1];
14234    atf_fuzz_Start(parent);
14235    (void)close(pipefd[1]);
14236    return read.fd;
14237}
14238
14239// --- command.atf_fuzz_proc.atf_fuzz.Kill
14240// Kill subprocess and wait
14241void command::atf_fuzz_Kill(command::atf_fuzz_proc& parent) {
14242    if (parent.pid != 0) {
14243        kill(parent.pid,9);
14244        atf_fuzz_Wait(parent);
14245    }
14246}
14247
14248// --- command.atf_fuzz_proc.atf_fuzz.Wait
14249// Wait for subprocess to return
14250void command::atf_fuzz_Wait(command::atf_fuzz_proc& parent) {
14251    if (parent.pid > 0) {
14252        int wait_flags = 0;
14253        int wait_status = 0;
14254        int rc = -1;
14255        do {
14256            // really wait for subprocess to exit
14257            rc = waitpid(parent.pid,&wait_status,wait_flags);
14258        } while (rc==-1 && errno==EINTR);
14259        if (rc == parent.pid) {
14260            parent.status = wait_status;
14261            parent.pid = 0;
14262        }
14263    }
14264}
14265
14266// --- command.atf_fuzz_proc.atf_fuzz.Exec
14267// Start + Wait
14268// Execute subprocess and return exit code
14269int command::atf_fuzz_Exec(command::atf_fuzz_proc& parent) {
14270    atf_fuzz_Start(parent);
14271    atf_fuzz_Wait(parent);
14272    return parent.status;
14273}
14274
14275// --- command.atf_fuzz_proc.atf_fuzz.ExecX
14276// Start + Wait, throw exception on error
14277// Execute subprocess; throw human-readable exception on error
14278void command::atf_fuzz_ExecX(command::atf_fuzz_proc& parent) {
14279    int rc = atf_fuzz_Exec(parent);
14280    vrfy(rc==0, tempstr() << "algo_lib.exec" << Keyval("cmd",atf_fuzz_ToCmdline(parent))
14281    << Keyval("comment",algo::DescribeWaitStatus(parent.status)));
14282}
14283
14284// --- command.atf_fuzz_proc.atf_fuzz.Execv
14285// Call execv()
14286// Call execv with specified parameters
14287int command::atf_fuzz_Execv(command::atf_fuzz_proc& parent) {
14288    int ret = 0;
14289    algo::StringAry args;
14290    atf_fuzz_ToArgv(parent, args);
14291    char **argv = (char**)alloca((ary_N(args)+1)*sizeof(*argv));
14292    ind_beg(algo::StringAry_ary_curs,arg,args) {
14293        argv[ind_curs(arg).index] = Zeroterm(arg);
14294    }ind_end;
14295    argv[ary_N(args)] = NULL;
14296    // if parent.path is relative, search for it in PATH
14297    algo_lib::ResolveExecFname(parent.path);
14298    ret = execv(Zeroterm(parent.path),argv);
14299    return ret;
14300}
14301
14302// --- command.atf_fuzz_proc.atf_fuzz.ToCmdline
14303algo::tempstr command::atf_fuzz_ToCmdline(command::atf_fuzz_proc& parent) {
14304    algo::tempstr retval;
14305    retval << parent.path << " ";
14306    command::atf_fuzz_PrintArgv(parent.cmd,retval);
14307    if (ch_N(parent.fstdin)) {
14308        retval << " " << parent.fstdin;
14309    }
14310    if (ch_N(parent.fstdout)) {
14311        retval << " " << parent.fstdout;
14312    }
14313    if (ch_N(parent.fstderr)) {
14314        retval << " 2" << parent.fstderr;
14315    }
14316    return retval;
14317}
14318
14319// --- command.atf_fuzz_proc.atf_fuzz.ToArgv
14320// Form array from the command line
14321void command::atf_fuzz_ToArgv(command::atf_fuzz_proc& parent, algo::StringAry& args) {
14322    ary_RemoveAll(args);
14323    ary_Alloc(args) << parent.path;
14324
14325    if (parent.cmd.reprofile != "temp/atf_fuzz.repro") {
14326        cstring *arg = &ary_Alloc(args);
14327        *arg << "-reprofile:";
14328        cstring_Print(parent.cmd.reprofile, *arg);
14329    }
14330
14331    if (parent.cmd.target != "") {
14332        cstring *arg = &ary_Alloc(args);
14333        *arg << "-target:";
14334        Smallstr16_Print(parent.cmd.target, *arg);
14335    }
14336
14337    if (parent.cmd.args != "") {
14338        cstring *arg = &ary_Alloc(args);
14339        *arg << "-args:";
14340        cstring_Print(parent.cmd.args, *arg);
14341    }
14342
14343    if (parent.cmd.inputfile != "") {
14344        cstring *arg = &ary_Alloc(args);
14345        *arg << "-inputfile:";
14346        cstring_Print(parent.cmd.inputfile, *arg);
14347    }
14348
14349    if (parent.cmd.fuzzstrat.expr != "%") {
14350        cstring *arg = &ary_Alloc(args);
14351        *arg << "-fuzzstrat:";
14352        command::fuzzstrat_Print(parent.cmd, *arg);
14353    }
14354
14355    if (parent.cmd.in != "data") {
14356        cstring *arg = &ary_Alloc(args);
14357        *arg << "-in:";
14358        cstring_Print(parent.cmd.in, *arg);
14359    }
14360
14361    if (parent.cmd.seed != 0) {
14362        cstring *arg = &ary_Alloc(args);
14363        *arg << "-seed:";
14364        i32_Print(parent.cmd.seed, *arg);
14365    }
14366
14367    if (parent.cmd.testprob != 1) {
14368        cstring *arg = &ary_Alloc(args);
14369        *arg << "-testprob:";
14370        double_Print(parent.cmd.testprob, *arg);
14371    }
14372    for (int i=1; i < algo_lib::_db.cmdline.verbose; ++i) {
14373        ary_Alloc(args) << "-verbose";
14374    }
14375}
14376
14377// --- command.atf_fuzz_proc..Uninit
14378void command::atf_fuzz_proc_Uninit(command::atf_fuzz_proc& parent) {
14379    command::atf_fuzz_proc &row = parent; (void)row;
14380
14381    // command.atf_fuzz_proc.atf_fuzz.Uninit (Exec)  //
14382    atf_fuzz_Kill(parent); // kill child, ensure forward progress
14383}
14384
14385// --- command.atf_gcli.gtblacttst.Print
14386// Print back to string
14387void command::gtblacttst_Print(command::atf_gcli& parent, algo::cstring &out) {
14388    Regx_Print(parent.gtblacttst, out);
14389}
14390
14391// --- command.atf_gcli.gtblacttst.ReadStrptrMaybe
14392// Read Regx from string
14393// Convert string to field. Return success value
14394bool command::gtblacttst_ReadStrptrMaybe(command::atf_gcli& parent, algo::strptr in) {
14395    bool retval = true;
14396    Regx_ReadSql(parent.gtblacttst, in, true);
14397    return retval;
14398}
14399
14400// --- command.atf_gcli..ReadFieldMaybe
14401bool command::atf_gcli_ReadFieldMaybe(command::atf_gcli& parent, algo::strptr field, algo::strptr strval) {
14402    bool retval = true;
14403    command::FieldId field_id;
14404    (void)value_SetStrptrMaybe(field_id,field);
14405    switch(field_id) {
14406        case command_FieldId_in: {
14407            retval = algo::cstring_ReadStrptrMaybe(parent.in, strval);
14408            break;
14409        }
14410        case command_FieldId_gtblacttst: {
14411            retval = gtblacttst_ReadStrptrMaybe(parent, strval);
14412            break;
14413        }
14414        case command_FieldId_id: {
14415            retval = algo::cstring_ReadStrptrMaybe(parent.id, strval);
14416            break;
14417        }
14418        case command_FieldId_mr: {
14419            retval = algo::cstring_ReadStrptrMaybe(parent.mr, strval);
14420            break;
14421        }
14422        case command_FieldId_note: {
14423            retval = algo::cstring_ReadStrptrMaybe(parent.note, strval);
14424            break;
14425        }
14426        case command_FieldId_capture: {
14427            retval = bool_ReadStrptrMaybe(parent.capture, strval);
14428            break;
14429        }
14430        case command_FieldId_skip_init: {
14431            retval = bool_ReadStrptrMaybe(parent.skip_init, strval);
14432            break;
14433        }
14434        case command_FieldId_skip_git_init: {
14435            retval = bool_ReadStrptrMaybe(parent.skip_git_init, strval);
14436            break;
14437        }
14438        case command_FieldId_dry_run: {
14439            retval = bool_ReadStrptrMaybe(parent.dry_run, strval);
14440            break;
14441        }
14442        default: break;
14443    }
14444    if (!retval) {
14445        algo_lib::AppendErrtext("attr",field);
14446    }
14447    return retval;
14448}
14449
14450// --- command.atf_gcli..ReadTupleMaybe
14451// Read fields of command::atf_gcli from attributes of ascii tuple TUPLE
14452bool command::atf_gcli_ReadTupleMaybe(command::atf_gcli &parent, algo::Tuple &tuple) {
14453    bool retval = true;
14454    ind_beg(algo::Tuple_attrs_curs,attr,tuple) {
14455        retval = atf_gcli_ReadFieldMaybe(parent, attr.name, attr.value);
14456        if (!retval) {
14457            break;
14458        }
14459    }ind_end;
14460    return retval;
14461}
14462
14463// --- command.atf_gcli..Init
14464// Set all fields to initial values.
14465void command::atf_gcli_Init(command::atf_gcli& parent) {
14466    parent.in = algo::strptr("data");
14467    Regx_ReadSql(parent.gtblacttst, "%", true);
14468    parent.id = algo::strptr("");
14469    parent.mr = algo::strptr("");
14470    parent.note = algo::strptr("");
14471    parent.capture = bool(false);
14472    parent.skip_init = bool(false);
14473    parent.skip_git_init = bool(false);
14474    parent.dry_run = bool(false);
14475}
14476
14477// --- command.atf_gcli..ToCmdline
14478// Convenience function that returns a full command line
14479// Assume command is in a directory called bin
14480tempstr command::atf_gcli_ToCmdline(command::atf_gcli& row) {
14481    tempstr ret;
14482    ret << "bin/atf_gcli ";
14483    atf_gcli_PrintArgv(row, ret);
14484    // inherit less intense verbose, debug options
14485    for (int i = 1; i < algo_lib::_db.cmdline.verbose; i++) {
14486        ret << " -verbose";
14487    }
14488    for (int i = 1; i < algo_lib::_db.cmdline.debug; i++) {
14489        ret << " -debug";
14490    }
14491    return ret;
14492}
14493
14494// --- command.atf_gcli..PrintArgv
14495// print string representation of ROW to string STR
14496// cfmt:command.atf_gcli.Argv  printfmt:Tuple
14497void command::atf_gcli_PrintArgv(command::atf_gcli& row, algo::cstring& str) {
14498    algo::tempstr temp;
14499    (void)temp;
14500    (void)str;
14501    if (!(row.in == "data")) {
14502        ch_RemoveAll(temp);
14503        cstring_Print(row.in, temp);
14504        str << " -in:";
14505        strptr_PrintBash(temp,str);
14506    }
14507    if (!(row.gtblacttst.expr == "%")) {
14508        ch_RemoveAll(temp);
14509        command::gtblacttst_Print(const_cast<command::atf_gcli&>(row), temp);
14510        str << " -gtblacttst:";
14511        strptr_PrintBash(temp,str);
14512    }
14513    if (!(row.id == "")) {
14514        ch_RemoveAll(temp);
14515        cstring_Print(row.id, temp);
14516        str << " -id:";
14517        strptr_PrintBash(temp,str);
14518    }
14519    if (!(row.mr == "")) {
14520        ch_RemoveAll(temp);
14521        cstring_Print(row.mr, temp);
14522        str << " -mr:";
14523        strptr_PrintBash(temp,str);
14524    }
14525    if (!(row.note == "")) {
14526        ch_RemoveAll(temp);
14527        cstring_Print(row.note, temp);
14528        str << " -note:";
14529        strptr_PrintBash(temp,str);
14530    }
14531    if (!(row.capture == false)) {
14532        ch_RemoveAll(temp);
14533        bool_Print(row.capture, temp);
14534        str << " -capture:";
14535        strptr_PrintBash(temp,str);
14536    }
14537    if (!(row.skip_init == false)) {
14538        ch_RemoveAll(temp);
14539        bool_Print(row.skip_init, temp);
14540        str << " -skip_init:";
14541        strptr_PrintBash(temp,str);
14542    }
14543    if (!(row.skip_git_init == false)) {
14544        ch_RemoveAll(temp);
14545        bool_Print(row.skip_git_init, temp);
14546        str << " -skip_git_init:";
14547        strptr_PrintBash(temp,str);
14548    }
14549    if (!(row.dry_run == false)) {
14550        ch_RemoveAll(temp);
14551        bool_Print(row.dry_run, temp);
14552        str << " -dry_run:";
14553        strptr_PrintBash(temp,str);
14554    }
14555}
14556
14557// --- command.atf_gcli..NArgs
14558// Used with command lines
14559// Return # of command-line arguments that must follow this argument
14560// If FIELD is invalid, return -1
14561i32 command::atf_gcli_NArgs(command::FieldId field, algo::strptr& out_dflt, bool* out_anon) {
14562    i32 retval = 1;
14563    switch (field) {
14564        case command_FieldId_in: { // $comment
14565            *out_anon = false;
14566        } break;
14567        case command_FieldId_gtblacttst: { // $comment
14568            *out_anon = false;
14569        } break;
14570        case command_FieldId_id: { // $comment
14571            *out_anon = false;
14572        } break;
14573        case command_FieldId_mr: { // $comment
14574            *out_anon = false;
14575        } break;
14576        case command_FieldId_note: { // $comment
14577            *out_anon = false;
14578        } break;
14579        case command_FieldId_capture: { // $comment
14580            *out_anon = false;
14581            retval=0;
14582            out_dflt="Y";
14583        } break;
14584        case command_FieldId_skip_init: { // bool: no argument required but value may be specified as capture:Y
14585            *out_anon = false;
14586            retval=0;
14587            out_dflt="Y";
14588        } break;
14589        case command_FieldId_skip_git_init: { // bool: no argument required but value may be specified as skip_init:Y
14590            *out_anon = false;
14591            retval=0;
14592            out_dflt="Y";
14593        } break;
14594        case command_FieldId_dry_run: { // bool: no argument required but value may be specified as skip_git_init:Y
14595            *out_anon = false;
14596            retval=0;
14597            out_dflt="Y";
14598        } break;
14599        default:
14600        retval=-1; // unrecognized
14601    }
14602    return retval;
14603}
14604
14605// --- command.atf_gcli_proc.atf_gcli.Start
14606// Start subprocess
14607// If subprocess already running, do nothing. Otherwise, start it
14608int command::atf_gcli_Start(command::atf_gcli_proc& parent) {
14609    int retval = 0;
14610    if (parent.pid == 0) {
14611        verblog(atf_gcli_ToCmdline(parent)); // maybe print command
14612#ifdef WIN32
14613        algo_lib::ResolveExecFname(parent.path);
14614        tempstr cmdline(atf_gcli_ToCmdline(parent));
14615        parent.pid = dospawn(Zeroterm(parent.path),Zeroterm(cmdline),parent.timeout,parent.fstdin,parent.fstdout,parent.fstderr);
14616#else
14617        parent.pid = fork();
14618        if (parent.pid == 0) { // child
14619            algo_lib::DieWithParent();
14620            if (parent.timeout > 0) {
14621                alarm(parent.timeout);
14622            }
14623            if (retval==0) retval=algo_lib::ApplyRedirect(parent.fstdin , 0);
14624            if (retval==0) retval=algo_lib::ApplyRedirect(parent.fstdout, 1);
14625            if (retval==0) retval=algo_lib::ApplyRedirect(parent.fstderr, 2);
14626            if (retval==0) retval= atf_gcli_Execv(parent);
14627            if (retval != 0) { // if start fails, print error
14628                int err=errno;
14629                prerr("command.atf_gcli_execv"
14630                <<Keyval("errno",err)
14631                <<Keyval("errstr",strerror(err))
14632                <<Keyval("comment","Execv failed"));
14633            }
14634            _exit(127); // if failed to start, exit anyway
14635        } else if (parent.pid == -1) {
14636            retval = errno; // failed to fork
14637        }
14638#endif
14639    }
14640    parent.status = parent.pid > 0 ? 0 : -1; // if didn't start, set error status
14641    return retval;
14642}
14643
14644// --- command.atf_gcli_proc.atf_gcli.StartRead
14645// Start subprocess & Read output
14646algo::Fildes command::atf_gcli_StartRead(command::atf_gcli_proc& parent, algo_lib::FFildes &read) {
14647    int pipefd[2];
14648    int rc=pipe(pipefd);
14649    (void)rc;
14650    read.fd.value = pipefd[0];
14651    parent.fstdout  << ">&" << pipefd[1];
14652    atf_gcli_Start(parent);
14653    (void)close(pipefd[1]);
14654    return read.fd;
14655}
14656
14657// --- command.atf_gcli_proc.atf_gcli.Kill
14658// Kill subprocess and wait
14659void command::atf_gcli_Kill(command::atf_gcli_proc& parent) {
14660    if (parent.pid != 0) {
14661        kill(parent.pid,9);
14662        atf_gcli_Wait(parent);
14663    }
14664}
14665
14666// --- command.atf_gcli_proc.atf_gcli.Wait
14667// Wait for subprocess to return
14668void command::atf_gcli_Wait(command::atf_gcli_proc& parent) {
14669    if (parent.pid > 0) {
14670        int wait_flags = 0;
14671        int wait_status = 0;
14672        int rc = -1;
14673        do {
14674            // really wait for subprocess to exit
14675            rc = waitpid(parent.pid,&wait_status,wait_flags);
14676        } while (rc==-1 && errno==EINTR);
14677        if (rc == parent.pid) {
14678            parent.status = wait_status;
14679            parent.pid = 0;
14680        }
14681    }
14682}
14683
14684// --- command.atf_gcli_proc.atf_gcli.Exec
14685// Start + Wait
14686// Execute subprocess and return exit code
14687int command::atf_gcli_Exec(command::atf_gcli_proc& parent) {
14688    atf_gcli_Start(parent);
14689    atf_gcli_Wait(parent);
14690    return parent.status;
14691}
14692
14693// --- command.atf_gcli_proc.atf_gcli.ExecX
14694// Start + Wait, throw exception on error
14695// Execute subprocess; throw human-readable exception on error
14696void command::atf_gcli_ExecX(command::atf_gcli_proc& parent) {
14697    int rc = atf_gcli_Exec(parent);
14698    vrfy(rc==0, tempstr() << "algo_lib.exec" << Keyval("cmd",atf_gcli_ToCmdline(parent))
14699    << Keyval("comment",algo::DescribeWaitStatus(parent.status)));
14700}
14701
14702// --- command.atf_gcli_proc.atf_gcli.Execv
14703// Call execv()
14704// Call execv with specified parameters
14705int command::atf_gcli_Execv(command::atf_gcli_proc& parent) {
14706    int ret = 0;
14707    algo::StringAry args;
14708    atf_gcli_ToArgv(parent, args);
14709    char **argv = (char**)alloca((ary_N(args)+1)*sizeof(*argv));
14710    ind_beg(algo::StringAry_ary_curs,arg,args) {
14711        argv[ind_curs(arg).index] = Zeroterm(arg);
14712    }ind_end;
14713    argv[ary_N(args)] = NULL;
14714    // if parent.path is relative, search for it in PATH
14715    algo_lib::ResolveExecFname(parent.path);
14716    ret = execv(Zeroterm(parent.path),argv);
14717    return ret;
14718}
14719
14720// --- command.atf_gcli_proc.atf_gcli.ToCmdline
14721algo::tempstr command::atf_gcli_ToCmdline(command::atf_gcli_proc& parent) {
14722    algo::tempstr retval;
14723    retval << parent.path << " ";
14724    command::atf_gcli_PrintArgv(parent.cmd,retval);
14725    if (ch_N(parent.fstdin)) {
14726        retval << " " << parent.fstdin;
14727    }
14728    if (ch_N(parent.fstdout)) {
14729        retval << " " << parent.fstdout;
14730    }
14731    if (ch_N(parent.fstderr)) {
14732        retval << " 2" << parent.fstderr;
14733    }
14734    return retval;
14735}
14736
14737// --- command.atf_gcli_proc.atf_gcli.ToArgv
14738// Form array from the command line
14739void command::atf_gcli_ToArgv(command::atf_gcli_proc& parent, algo::StringAry& args) {
14740    ary_RemoveAll(args);
14741    ary_Alloc(args) << parent.path;
14742
14743    if (parent.cmd.in != "data") {
14744        cstring *arg = &ary_Alloc(args);
14745        *arg << "-in:";
14746        cstring_Print(parent.cmd.in, *arg);
14747    }
14748
14749    if (parent.cmd.gtblacttst.expr != "%") {
14750        cstring *arg = &ary_Alloc(args);
14751        *arg << "-gtblacttst:";
14752        command::gtblacttst_Print(parent.cmd, *arg);
14753    }
14754
14755    if (parent.cmd.id != "") {
14756        cstring *arg = &ary_Alloc(args);
14757        *arg << "-id:";
14758        cstring_Print(parent.cmd.id, *arg);
14759    }
14760
14761    if (parent.cmd.mr != "") {
14762        cstring *arg = &ary_Alloc(args);
14763        *arg << "-mr:";
14764        cstring_Print(parent.cmd.mr, *arg);
14765    }
14766
14767    if (parent.cmd.note != "") {
14768        cstring *arg = &ary_Alloc(args);
14769        *arg << "-note:";
14770        cstring_Print(parent.cmd.note, *arg);
14771    }
14772
14773    if (parent.cmd.capture != false) {
14774        cstring *arg = &ary_Alloc(args);
14775        *arg << "-capture:";
14776        bool_Print(parent.cmd.capture, *arg);
14777    }
14778
14779    if (parent.cmd.skip_init != false) {
14780        cstring *arg = &ary_Alloc(args);
14781        *arg << "-skip_init:";
14782        bool_Print(parent.cmd.skip_init, *arg);
14783    }
14784
14785    if (parent.cmd.skip_git_init != false) {
14786        cstring *arg = &ary_Alloc(args);
14787        *arg << "-skip_git_init:";
14788        bool_Print(parent.cmd.skip_git_init, *arg);
14789    }
14790
14791    if (parent.cmd.dry_run != false) {
14792        cstring *arg = &ary_Alloc(args);
14793        *arg << "-dry_run:";
14794        bool_Print(parent.cmd.dry_run, *arg);
14795    }
14796    for (int i=1; i < algo_lib::_db.cmdline.verbose; ++i) {
14797        ary_Alloc(args) << "-verbose";
14798    }
14799}
14800
14801// --- command.atf_gcli_proc..Uninit
14802void command::atf_gcli_proc_Uninit(command::atf_gcli_proc& parent) {
14803    command::atf_gcli_proc &row = parent; (void)row;
14804
14805    // command.atf_gcli_proc.atf_gcli.Uninit (Exec)  //
14806    atf_gcli_Kill(parent); // kill child, ensure forward progress
14807}
14808
14809// --- command.atf_netio..ReadFieldMaybe
14810bool command::atf_netio_ReadFieldMaybe(command::atf_netio& parent, algo::strptr field, algo::strptr strval) {
14811    bool retval = true;
14812    command::FieldId field_id;
14813    (void)value_SetStrptrMaybe(field_id,field);
14814    switch(field_id) {
14815        case command_FieldId_in: {
14816            retval = algo::cstring_ReadStrptrMaybe(parent.in, strval);
14817            break;
14818        }
14819        case command_FieldId_randomize_ports: {
14820            retval = bool_ReadStrptrMaybe(parent.randomize_ports, strval);
14821            break;
14822        }
14823        default: break;
14824    }
14825    if (!retval) {
14826        algo_lib::AppendErrtext("attr",field);
14827    }
14828    return retval;
14829}
14830
14831// --- command.atf_netio..ReadTupleMaybe
14832// Read fields of command::atf_netio from attributes of ascii tuple TUPLE
14833bool command::atf_netio_ReadTupleMaybe(command::atf_netio &parent, algo::Tuple &tuple) {
14834    bool retval = true;
14835    ind_beg(algo::Tuple_attrs_curs,attr,tuple) {
14836        retval = atf_netio_ReadFieldMaybe(parent, attr.name, attr.value);
14837        if (!retval) {
14838            break;
14839        }
14840    }ind_end;
14841    return retval;
14842}
14843
14844// --- command.atf_netio..ToCmdline
14845// Convenience function that returns a full command line
14846// Assume command is in a directory called bin
14847tempstr command::atf_netio_ToCmdline(command::atf_netio& row) {
14848    tempstr ret;
14849    ret << "bin/atf_netio ";
14850    atf_netio_PrintArgv(row, ret);
14851    // inherit less intense verbose, debug options
14852    for (int i = 1; i < algo_lib::_db.cmdline.verbose; i++) {
14853        ret << " -verbose";
14854    }
14855    for (int i = 1; i < algo_lib::_db.cmdline.debug; i++) {
14856        ret << " -debug";
14857    }
14858    return ret;
14859}
14860
14861// --- command.atf_netio..PrintArgv
14862// print string representation of ROW to string STR
14863// cfmt:command.atf_netio.Argv  printfmt:Tuple
14864void command::atf_netio_PrintArgv(command::atf_netio& row, algo::cstring& str) {
14865    algo::tempstr temp;
14866    (void)temp;
14867    (void)str;
14868    if (!(row.in == "data")) {
14869        ch_RemoveAll(temp);
14870        cstring_Print(row.in, temp);
14871        str << " -in:";
14872        strptr_PrintBash(temp,str);
14873    }
14874    if (!(row.randomize_ports == true)) {
14875        ch_RemoveAll(temp);
14876        bool_Print(row.randomize_ports, temp);
14877        str << " -randomize_ports:";
14878        strptr_PrintBash(temp,str);
14879    }
14880}
14881
14882// --- command.atf_netio..NArgs
14883// Used with command lines
14884// Return # of command-line arguments that must follow this argument
14885// If FIELD is invalid, return -1
14886i32 command::atf_netio_NArgs(command::FieldId field, algo::strptr& out_dflt, bool* out_anon) {
14887    i32 retval = 1;
14888    switch (field) {
14889        case command_FieldId_in: { // $comment
14890            *out_anon = false;
14891        } break;
14892        case command_FieldId_randomize_ports: { // $comment
14893            *out_anon = false;
14894            retval=0;
14895            out_dflt="Y";
14896        } break;
14897        default:
14898        retval=-1; // unrecognized
14899    }
14900    return retval;
14901}
14902
14903// --- command.atf_netio_proc.atf_netio.Start
14904// Start subprocess
14905// If subprocess already running, do nothing. Otherwise, start it
14906int command::atf_netio_Start(command::atf_netio_proc& parent) {
14907    int retval = 0;
14908    if (parent.pid == 0) {
14909        verblog(atf_netio_ToCmdline(parent)); // maybe print command
14910#ifdef WIN32
14911        algo_lib::ResolveExecFname(parent.path);
14912        tempstr cmdline(atf_netio_ToCmdline(parent));
14913        parent.pid = dospawn(Zeroterm(parent.path),Zeroterm(cmdline),parent.timeout,parent.fstdin,parent.fstdout,parent.fstderr);
14914#else
14915        parent.pid = fork();
14916        if (parent.pid == 0) { // child
14917            algo_lib::DieWithParent();
14918            if (parent.timeout > 0) {
14919                alarm(parent.timeout);
14920            }
14921            if (retval==0) retval=algo_lib::ApplyRedirect(parent.fstdin , 0);
14922            if (retval==0) retval=algo_lib::ApplyRedirect(parent.fstdout, 1);
14923            if (retval==0) retval=algo_lib::ApplyRedirect(parent.fstderr, 2);
14924            if (retval==0) retval= atf_netio_Execv(parent);
14925            if (retval != 0) { // if start fails, print error
14926                int err=errno;
14927                prerr("command.atf_netio_execv"
14928                <<Keyval("errno",err)
14929                <<Keyval("errstr",strerror(err))
14930                <<Keyval("comment","Execv failed"));
14931            }
14932            _exit(127); // if failed to start, exit anyway
14933        } else if (parent.pid == -1) {
14934            retval = errno; // failed to fork
14935        }
14936#endif
14937    }
14938    parent.status = parent.pid > 0 ? 0 : -1; // if didn't start, set error status
14939    return retval;
14940}
14941
14942// --- command.atf_netio_proc.atf_netio.StartRead
14943// Start subprocess & Read output
14944algo::Fildes command::atf_netio_StartRead(command::atf_netio_proc& parent, algo_lib::FFildes &read) {
14945    int pipefd[2];
14946    int rc=pipe(pipefd);
14947    (void)rc;
14948    read.fd.value = pipefd[0];
14949    parent.fstdout  << ">&" << pipefd[1];
14950    atf_netio_Start(parent);
14951    (void)close(pipefd[1]);
14952    return read.fd;
14953}
14954
14955// --- command.atf_netio_proc.atf_netio.Kill
14956// Kill subprocess and wait
14957void command::atf_netio_Kill(command::atf_netio_proc& parent) {
14958    if (parent.pid != 0) {
14959        kill(parent.pid,9);
14960        atf_netio_Wait(parent);
14961    }
14962}
14963
14964// --- command.atf_netio_proc.atf_netio.Wait
14965// Wait for subprocess to return
14966void command::atf_netio_Wait(command::atf_netio_proc& parent) {
14967    if (parent.pid > 0) {
14968        int wait_flags = 0;
14969        int wait_status = 0;
14970        int rc = -1;
14971        do {
14972            // really wait for subprocess to exit
14973            rc = waitpid(parent.pid,&wait_status,wait_flags);
14974        } while (rc==-1 && errno==EINTR);
14975        if (rc == parent.pid) {
14976            parent.status = wait_status;
14977            parent.pid = 0;
14978        }
14979    }
14980}
14981
14982// --- command.atf_netio_proc.atf_netio.Exec
14983// Start + Wait
14984// Execute subprocess and return exit code
14985int command::atf_netio_Exec(command::atf_netio_proc& parent) {
14986    atf_netio_Start(parent);
14987    atf_netio_Wait(parent);
14988    return parent.status;
14989}
14990
14991// --- command.atf_netio_proc.atf_netio.ExecX
14992// Start + Wait, throw exception on error
14993// Execute subprocess; throw human-readable exception on error
14994void command::atf_netio_ExecX(command::atf_netio_proc& parent) {
14995    int rc = atf_netio_Exec(parent);
14996    vrfy(rc==0, tempstr() << "algo_lib.exec" << Keyval("cmd",atf_netio_ToCmdline(parent))
14997    << Keyval("comment",algo::DescribeWaitStatus(parent.status)));
14998}
14999
15000// --- command.atf_netio_proc.atf_netio.Execv
15001// Call execv()
15002// Call execv with specified parameters
15003int command::atf_netio_Execv(command::atf_netio_proc& parent) {
15004    int ret = 0;
15005    algo::StringAry args;
15006    atf_netio_ToArgv(parent, args);
15007    char **argv = (char**)alloca((ary_N(args)+1)*sizeof(*argv));
15008    ind_beg(algo::StringAry_ary_curs,arg,args) {
15009        argv[ind_curs(arg).index] = Zeroterm(arg);
15010    }ind_end;
15011    argv[ary_N(args)] = NULL;
15012    // if parent.path is relative, search for it in PATH
15013    algo_lib::ResolveExecFname(parent.path);
15014    ret = execv(Zeroterm(parent.path),argv);
15015    return ret;
15016}
15017
15018// --- command.atf_netio_proc.atf_netio.ToCmdline
15019algo::tempstr command::atf_netio_ToCmdline(command::atf_netio_proc& parent) {
15020    algo::tempstr retval;
15021    retval << parent.path << " ";
15022    command::atf_netio_PrintArgv(parent.cmd,retval);
15023    if (ch_N(parent.fstdin)) {
15024        retval << " " << parent.fstdin;
15025    }
15026    if (ch_N(parent.fstdout)) {
15027        retval << " " << parent.fstdout;
15028    }
15029    if (ch_N(parent.fstderr)) {
15030        retval << " 2" << parent.fstderr;
15031    }
15032    return retval;
15033}
15034
15035// --- command.atf_netio_proc.atf_netio.ToArgv
15036// Form array from the command line
15037void command::atf_netio_ToArgv(command::atf_netio_proc& parent, algo::StringAry& args) {
15038    ary_RemoveAll(args);
15039    ary_Alloc(args) << parent.path;
15040
15041    if (parent.cmd.in != "data") {
15042        cstring *arg = &ary_Alloc(args);
15043        *arg << "-in:";
15044        cstring_Print(parent.cmd.in, *arg);
15045    }
15046
15047    if (parent.cmd.randomize_ports != true) {
15048        cstring *arg = &ary_Alloc(args);
15049        *arg << "-randomize_ports:";
15050        bool_Print(parent.cmd.randomize_ports, *arg);
15051    }
15052    for (int i=1; i < algo_lib::_db.cmdline.verbose; ++i) {
15053        ary_Alloc(args) << "-verbose";
15054    }
15055}
15056
15057// --- command.atf_netio_proc..Uninit
15058void command::atf_netio_proc_Uninit(command::atf_netio_proc& parent) {
15059    command::atf_netio_proc &row = parent; (void)row;
15060
15061    // command.atf_netio_proc.atf_netio.Uninit (Exec)  //
15062    atf_netio_Kill(parent); // kill child, ensure forward progress
15063}
15064
15065// --- command.atf_nrun..ReadFieldMaybe
15066bool command::atf_nrun_ReadFieldMaybe(command::atf_nrun& parent, algo::strptr field, algo::strptr strval) {
15067    bool retval = true;
15068    command::FieldId field_id;
15069    (void)value_SetStrptrMaybe(field_id,field);
15070    switch(field_id) {
15071        case command_FieldId_in: {
15072            retval = algo::cstring_ReadStrptrMaybe(parent.in, strval);
15073            break;
15074        }
15075        case command_FieldId_maxjobs: {
15076            retval = i32_ReadStrptrMaybe(parent.maxjobs, strval);
15077            break;
15078        }
15079        case command_FieldId_ncmd: {
15080            retval = i32_ReadStrptrMaybe(parent.ncmd, strval);
15081            break;
15082        }
15083        default: break;
15084    }
15085    if (!retval) {
15086        algo_lib::AppendErrtext("attr",field);
15087    }
15088    return retval;
15089}
15090
15091// --- command.atf_nrun..ReadTupleMaybe
15092// Read fields of command::atf_nrun from attributes of ascii tuple TUPLE
15093bool command::atf_nrun_ReadTupleMaybe(command::atf_nrun &parent, algo::Tuple &tuple) {
15094    bool retval = true;
15095    int anon_idx = 0;
15096    ind_beg(algo::Tuple_attrs_curs,attr,tuple) {
15097        if (ch_N(attr.name) == 0) {
15098            attr.name = atf_nrun_GetAnon(parent, anon_idx++);
15099        }
15100        retval = atf_nrun_ReadFieldMaybe(parent, attr.name, attr.value);
15101        if (!retval) {
15102            break;
15103        }
15104    }ind_end;
15105    return retval;
15106}
15107
15108// --- command.atf_nrun..ToCmdline
15109// Convenience function that returns a full command line
15110// Assume command is in a directory called bin
15111tempstr command::atf_nrun_ToCmdline(command::atf_nrun& row) {
15112    tempstr ret;
15113    ret << "bin/atf_nrun ";
15114    atf_nrun_PrintArgv(row, ret);
15115    // inherit less intense verbose, debug options
15116    for (int i = 1; i < algo_lib::_db.cmdline.verbose; i++) {
15117        ret << " -verbose";
15118    }
15119    for (int i = 1; i < algo_lib::_db.cmdline.debug; i++) {
15120        ret << " -debug";
15121    }
15122    return ret;
15123}
15124
15125// --- command.atf_nrun..PrintArgv
15126// print string representation of ROW to string STR
15127// cfmt:command.atf_nrun.Argv  printfmt:Tuple
15128void command::atf_nrun_PrintArgv(command::atf_nrun& row, algo::cstring& str) {
15129    algo::tempstr temp;
15130    (void)temp;
15131    (void)str;
15132    if (!(row.in == "data")) {
15133        ch_RemoveAll(temp);
15134        cstring_Print(row.in, temp);
15135        str << " -in:";
15136        strptr_PrintBash(temp,str);
15137    }
15138    if (!(row.maxjobs == 2)) {
15139        ch_RemoveAll(temp);
15140        i32_Print(row.maxjobs, temp);
15141        str << " -maxjobs:";
15142        strptr_PrintBash(temp,str);
15143    }
15144    ch_RemoveAll(temp);
15145    i32_Print(row.ncmd, temp);
15146    str << " -ncmd:";
15147    strptr_PrintBash(temp,str);
15148}
15149
15150// --- command.atf_nrun..GetAnon
15151algo::strptr command::atf_nrun_GetAnon(command::atf_nrun &parent, i32 idx) {
15152    (void)parent;//only to avoid -Wunused-parameter
15153    switch(idx) {
15154        case(0): return strptr("ncmd", 4);
15155        default: return algo::strptr();
15156    }
15157}
15158
15159// --- command.atf_nrun..NArgs
15160// Used with command lines
15161// Return # of command-line arguments that must follow this argument
15162// If FIELD is invalid, return -1
15163i32 command::atf_nrun_NArgs(command::FieldId field, algo::strptr& out_dflt, bool* out_anon) {
15164    i32 retval = 1;
15165    switch (field) {
15166        case command_FieldId_in: { // $comment
15167            *out_anon = false;
15168        } break;
15169        case command_FieldId_maxjobs: { // $comment
15170            *out_anon = false;
15171        } break;
15172        case command_FieldId_ncmd: { // $comment
15173            *out_anon = true;
15174        } break;
15175        default:
15176        retval=-1; // unrecognized
15177    }
15178    (void)out_dflt;//only to avoid -Wunused-parameter
15179    return retval;
15180}
15181
15182// --- command.atf_nrun_proc.atf_nrun.Start
15183// Start subprocess
15184// If subprocess already running, do nothing. Otherwise, start it
15185int command::atf_nrun_Start(command::atf_nrun_proc& parent) {
15186    int retval = 0;
15187    if (parent.pid == 0) {
15188        verblog(atf_nrun_ToCmdline(parent)); // maybe print command
15189#ifdef WIN32
15190        algo_lib::ResolveExecFname(parent.path);
15191        tempstr cmdline(atf_nrun_ToCmdline(parent));
15192        parent.pid = dospawn(Zeroterm(parent.path),Zeroterm(cmdline),parent.timeout,parent.fstdin,parent.fstdout,parent.fstderr);
15193#else
15194        parent.pid = fork();
15195        if (parent.pid == 0) { // child
15196            algo_lib::DieWithParent();
15197            if (parent.timeout > 0) {
15198                alarm(parent.timeout);
15199            }
15200            if (retval==0) retval=algo_lib::ApplyRedirect(parent.fstdin , 0);
15201            if (retval==0) retval=algo_lib::ApplyRedirect(parent.fstdout, 1);
15202            if (retval==0) retval=algo_lib::ApplyRedirect(parent.fstderr, 2);
15203            if (retval==0) retval= atf_nrun_Execv(parent);
15204            if (retval != 0) { // if start fails, print error
15205                int err=errno;
15206                prerr("command.atf_nrun_execv"
15207                <<Keyval("errno",err)
15208                <<Keyval("errstr",strerror(err))
15209                <<Keyval("comment","Execv failed"));
15210            }
15211            _exit(127); // if failed to start, exit anyway
15212        } else if (parent.pid == -1) {
15213            retval = errno; // failed to fork
15214        }
15215#endif
15216    }
15217    parent.status = parent.pid > 0 ? 0 : -1; // if didn't start, set error status
15218    return retval;
15219}
15220
15221// --- command.atf_nrun_proc.atf_nrun.StartRead
15222// Start subprocess & Read output
15223algo::Fildes command::atf_nrun_StartRead(command::atf_nrun_proc& parent, algo_lib::FFildes &read) {
15224    int pipefd[2];
15225    int rc=pipe(pipefd);
15226    (void)rc;
15227    read.fd.value = pipefd[0];
15228    parent.fstdout  << ">&" << pipefd[1];
15229    atf_nrun_Start(parent);
15230    (void)close(pipefd[1]);
15231    return read.fd;
15232}
15233
15234// --- command.atf_nrun_proc.atf_nrun.Kill
15235// Kill subprocess and wait
15236void command::atf_nrun_Kill(command::atf_nrun_proc& parent) {
15237    if (parent.pid != 0) {
15238        kill(parent.pid,9);
15239        atf_nrun_Wait(parent);
15240    }
15241}
15242
15243// --- command.atf_nrun_proc.atf_nrun.Wait
15244// Wait for subprocess to return
15245void command::atf_nrun_Wait(command::atf_nrun_proc& parent) {
15246    if (parent.pid > 0) {
15247        int wait_flags = 0;
15248        int wait_status = 0;
15249        int rc = -1;
15250        do {
15251            // really wait for subprocess to exit
15252            rc = waitpid(parent.pid,&wait_status,wait_flags);
15253        } while (rc==-1 && errno==EINTR);
15254        if (rc == parent.pid) {
15255            parent.status = wait_status;
15256            parent.pid = 0;
15257        }
15258    }
15259}
15260
15261// --- command.atf_nrun_proc.atf_nrun.Exec
15262// Start + Wait
15263// Execute subprocess and return exit code
15264int command::atf_nrun_Exec(command::atf_nrun_proc& parent) {
15265    atf_nrun_Start(parent);
15266    atf_nrun_Wait(parent);
15267    return parent.status;
15268}
15269
15270// --- command.atf_nrun_proc.atf_nrun.ExecX
15271// Start + Wait, throw exception on error
15272// Execute subprocess; throw human-readable exception on error
15273void command::atf_nrun_ExecX(command::atf_nrun_proc& parent) {
15274    int rc = atf_nrun_Exec(parent);
15275    vrfy(rc==0, tempstr() << "algo_lib.exec" << Keyval("cmd",atf_nrun_ToCmdline(parent))
15276    << Keyval("comment",algo::DescribeWaitStatus(parent.status)));
15277}
15278
15279// --- command.atf_nrun_proc.atf_nrun.Execv
15280// Call execv()
15281// Call execv with specified parameters
15282int command::atf_nrun_Execv(command::atf_nrun_proc& parent) {
15283    int ret = 0;
15284    algo::StringAry args;
15285    atf_nrun_ToArgv(parent, args);
15286    char **argv = (char**)alloca((ary_N(args)+1)*sizeof(*argv));
15287    ind_beg(algo::StringAry_ary_curs,arg,args) {
15288        argv[ind_curs(arg).index] = Zeroterm(arg);
15289    }ind_end;
15290    argv[ary_N(args)] = NULL;
15291    // if parent.path is relative, search for it in PATH
15292    algo_lib::ResolveExecFname(parent.path);
15293    ret = execv(Zeroterm(parent.path),argv);
15294    return ret;
15295}
15296
15297// --- command.atf_nrun_proc.atf_nrun.ToCmdline
15298algo::tempstr command::atf_nrun_ToCmdline(command::atf_nrun_proc& parent) {
15299    algo::tempstr retval;
15300    retval << parent.path << " ";
15301    command::atf_nrun_PrintArgv(parent.cmd,retval);
15302    if (ch_N(parent.fstdin)) {
15303        retval << " " << parent.fstdin;
15304    }
15305    if (ch_N(parent.fstdout)) {
15306        retval << " " << parent.fstdout;
15307    }
15308    if (ch_N(parent.fstderr)) {
15309        retval << " 2" << parent.fstderr;
15310    }
15311    return retval;
15312}
15313
15314// --- command.atf_nrun_proc.atf_nrun.ToArgv
15315// Form array from the command line
15316void command::atf_nrun_ToArgv(command::atf_nrun_proc& parent, algo::StringAry& args) {
15317    ary_RemoveAll(args);
15318    ary_Alloc(args) << parent.path;
15319
15320    if (parent.cmd.in != "data") {
15321        cstring *arg = &ary_Alloc(args);
15322        *arg << "-in:";
15323        cstring_Print(parent.cmd.in, *arg);
15324    }
15325
15326    if (parent.cmd.maxjobs != 2) {
15327        cstring *arg = &ary_Alloc(args);
15328        *arg << "-maxjobs:";
15329        i32_Print(parent.cmd.maxjobs, *arg);
15330    }
15331
15332    if (parent.cmd.ncmd != 6) {
15333        cstring *arg = &ary_Alloc(args);
15334        *arg << "-ncmd:";
15335        i32_Print(parent.cmd.ncmd, *arg);
15336    }
15337    for (int i=1; i < algo_lib::_db.cmdline.verbose; ++i) {
15338        ary_Alloc(args) << "-verbose";
15339    }
15340}
15341
15342// --- command.atf_nrun_proc..Uninit
15343void command::atf_nrun_proc_Uninit(command::atf_nrun_proc& parent) {
15344    command::atf_nrun_proc &row = parent; (void)row;
15345
15346    // command.atf_nrun_proc.atf_nrun.Uninit (Exec)  //
15347    atf_nrun_Kill(parent); // kill child, ensure forward progress
15348}
15349
15350// --- command.atf_unit.unittest.Print
15351// Print back to string
15352void command::unittest_Print(command::atf_unit& parent, algo::cstring &out) {
15353    Regx_Print(parent.unittest, out);
15354}
15355
15356// --- command.atf_unit.unittest.ReadStrptrMaybe
15357// Read Regx from string
15358// Convert string to field. Return success value
15359bool command::unittest_ReadStrptrMaybe(command::atf_unit& parent, algo::strptr in) {
15360    bool retval = true;
15361    Regx_ReadSql(parent.unittest, in, true);
15362    return retval;
15363}
15364
15365// --- command.atf_unit..ReadFieldMaybe
15366bool command::atf_unit_ReadFieldMaybe(command::atf_unit& parent, algo::strptr field, algo::strptr strval) {
15367    bool retval = true;
15368    command::FieldId field_id;
15369    (void)value_SetStrptrMaybe(field_id,field);
15370    switch(field_id) {
15371        case command_FieldId_unittest: {
15372            retval = unittest_ReadStrptrMaybe(parent, strval);
15373            break;
15374        }
15375        case command_FieldId_nofork: {
15376            retval = bool_ReadStrptrMaybe(parent.nofork, strval);
15377            break;
15378        }
15379        case command_FieldId_arg: {
15380            retval = algo::cstring_ReadStrptrMaybe(parent.arg, strval);
15381            break;
15382        }
15383        case command_FieldId_data_dir: {
15384            retval = algo::cstring_ReadStrptrMaybe(parent.data_dir, strval);
15385            break;
15386        }
15387        case command_FieldId_mdbg: {
15388            retval = bool_ReadStrptrMaybe(parent.mdbg, strval);
15389            break;
15390        }
15391        case command_FieldId_perf_secs: {
15392            retval = double_ReadStrptrMaybe(parent.perf_secs, strval);
15393            break;
15394        }
15395        case command_FieldId_pertest_timeout: {
15396            retval = u32_ReadStrptrMaybe(parent.pertest_timeout, strval);
15397            break;
15398        }
15399        case command_FieldId_report: {
15400            retval = bool_ReadStrptrMaybe(parent.report, strval);
15401            break;
15402        }
15403        case command_FieldId_capture: {
15404            retval = bool_ReadStrptrMaybe(parent.capture, strval);
15405            break;
15406        }
15407        case command_FieldId_check_untracked: {
15408            retval = bool_ReadStrptrMaybe(parent.check_untracked, strval);
15409            break;
15410        }
15411        default: break;
15412    }
15413    if (!retval) {
15414        algo_lib::AppendErrtext("attr",field);
15415    }
15416    return retval;
15417}
15418
15419// --- command.atf_unit..ReadTupleMaybe
15420// Read fields of command::atf_unit from attributes of ascii tuple TUPLE
15421bool command::atf_unit_ReadTupleMaybe(command::atf_unit &parent, algo::Tuple &tuple) {
15422    bool retval = true;
15423    int anon_idx = 0;
15424    ind_beg(algo::Tuple_attrs_curs,attr,tuple) {
15425        if (ch_N(attr.name) == 0) {
15426            attr.name = atf_unit_GetAnon(parent, anon_idx++);
15427        }
15428        retval = atf_unit_ReadFieldMaybe(parent, attr.name, attr.value);
15429        if (!retval) {
15430            break;
15431        }
15432    }ind_end;
15433    return retval;
15434}
15435
15436// --- command.atf_unit..Init
15437// Set all fields to initial values.
15438void command::atf_unit_Init(command::atf_unit& parent) {
15439    Regx_ReadSql(parent.unittest, "%", true);
15440    parent.nofork = bool(false);
15441    parent.arg = algo::strptr("");
15442    parent.data_dir = algo::strptr("data");
15443    parent.mdbg = bool(0);
15444    parent.perf_secs = double(1.0);
15445    parent.pertest_timeout = u32(900);
15446    parent.report = bool(true);
15447    parent.capture = bool(false);
15448    parent.check_untracked = bool(true);
15449}
15450
15451// --- command.atf_unit..ToCmdline
15452// Convenience function that returns a full command line
15453// Assume command is in a directory called bin
15454tempstr command::atf_unit_ToCmdline(command::atf_unit& row) {
15455    tempstr ret;
15456    ret << "bin/atf_unit ";
15457    atf_unit_PrintArgv(row, ret);
15458    // inherit less intense verbose, debug options
15459    for (int i = 1; i < algo_lib::_db.cmdline.verbose; i++) {
15460        ret << " -verbose";
15461    }
15462    for (int i = 1; i < algo_lib::_db.cmdline.debug; i++) {
15463        ret << " -debug";
15464    }
15465    return ret;
15466}
15467
15468// --- command.atf_unit..PrintArgv
15469// print string representation of ROW to string STR
15470// cfmt:command.atf_unit.Argv  printfmt:Auto
15471void command::atf_unit_PrintArgv(command::atf_unit& row, algo::cstring& str) {
15472    algo::tempstr temp;
15473    (void)temp;
15474    (void)str;
15475    ch_RemoveAll(temp);
15476    command::unittest_Print(const_cast<command::atf_unit&>(row), temp);
15477    str << " -unittest:";
15478    strptr_PrintBash(temp,str);
15479    if (!(row.nofork == false)) {
15480        ch_RemoveAll(temp);
15481        bool_Print(row.nofork, temp);
15482        str << " -nofork:";
15483        strptr_PrintBash(temp,str);
15484    }
15485    if (!(row.arg == "")) {
15486        ch_RemoveAll(temp);
15487        cstring_Print(row.arg, temp);
15488        str << " -arg:";
15489        strptr_PrintBash(temp,str);
15490    }
15491    if (!(row.data_dir == "data")) {
15492        ch_RemoveAll(temp);
15493        cstring_Print(row.data_dir, temp);
15494        str << " -data_dir:";
15495        strptr_PrintBash(temp,str);
15496    }
15497    if (!(row.mdbg == 0)) {
15498        ch_RemoveAll(temp);
15499        bool_Print(row.mdbg, temp);
15500        str << " -mdbg:";
15501        strptr_PrintBash(temp,str);
15502    }
15503    if (!(row.perf_secs == 1.0)) {
15504        ch_RemoveAll(temp);
15505        double_Print(row.perf_secs, temp);
15506        str << " -perf_secs:";
15507        strptr_PrintBash(temp,str);
15508    }
15509    if (!(row.pertest_timeout == 900)) {
15510        ch_RemoveAll(temp);
15511        u32_Print(row.pertest_timeout, temp);
15512        str << " -pertest_timeout:";
15513        strptr_PrintBash(temp,str);
15514    }
15515    if (!(row.report == true)) {
15516        ch_RemoveAll(temp);
15517        bool_Print(row.report, temp);
15518        str << " -report:";
15519        strptr_PrintBash(temp,str);
15520    }
15521    if (!(row.capture == false)) {
15522        ch_RemoveAll(temp);
15523        bool_Print(row.capture, temp);
15524        str << " -capture:";
15525        strptr_PrintBash(temp,str);
15526    }
15527    if (!(row.check_untracked == true)) {
15528        ch_RemoveAll(temp);
15529        bool_Print(row.check_untracked, temp);
15530        str << " -check_untracked:";
15531        strptr_PrintBash(temp,str);
15532    }
15533}
15534
15535// --- command.atf_unit..GetAnon
15536algo::strptr command::atf_unit_GetAnon(command::atf_unit &parent, i32 idx) {
15537    (void)parent;//only to avoid -Wunused-parameter
15538    switch(idx) {
15539        case(0): return strptr("unittest", 8);
15540        default: return algo::strptr();
15541    }
15542}
15543
15544// --- command.atf_unit..NArgs
15545// Used with command lines
15546// Return # of command-line arguments that must follow this argument
15547// If FIELD is invalid, return -1
15548i32 command::atf_unit_NArgs(command::FieldId field, algo::strptr& out_dflt, bool* out_anon) {
15549    i32 retval = 1;
15550    switch (field) {
15551        case command_FieldId_unittest: { // $comment
15552            *out_anon = true;
15553        } break;
15554        case command_FieldId_nofork: { // $comment
15555            *out_anon = false;
15556            retval=0;
15557            out_dflt="Y";
15558        } break;
15559        case command_FieldId_arg: { // bool: no argument required but value may be specified as nofork:Y
15560            *out_anon = false;
15561        } break;
15562        case command_FieldId_data_dir: { // bool: no argument required but value may be specified as nofork:Y
15563            *out_anon = false;
15564        } break;
15565        case command_FieldId_mdbg: { // bool: no argument required but value may be specified as nofork:Y
15566            *out_anon = false;
15567            retval=0;
15568            out_dflt="Y";
15569        } break;
15570        case command_FieldId_perf_secs: { // bool: no argument required but value may be specified as mdbg:Y
15571            *out_anon = false;
15572        } break;
15573        case command_FieldId_pertest_timeout: { // bool: no argument required but value may be specified as mdbg:Y
15574            *out_anon = false;
15575        } break;
15576        case command_FieldId_report: { // bool: no argument required but value may be specified as mdbg:Y
15577            *out_anon = false;
15578            retval=0;
15579            out_dflt="Y";
15580        } break;
15581        case command_FieldId_capture: { // bool: no argument required but value may be specified as report:Y
15582            *out_anon = false;
15583            retval=0;
15584            out_dflt="Y";
15585        } break;
15586        case command_FieldId_check_untracked: { // bool: no argument required but value may be specified as capture:Y
15587            *out_anon = false;
15588            retval=0;
15589            out_dflt="Y";
15590        } break;
15591        default:
15592        retval=-1; // unrecognized
15593    }
15594    return retval;
15595}
15596
15597// --- command.atf_unit_proc.atf_unit.Start
15598// Start subprocess
15599// If subprocess already running, do nothing. Otherwise, start it
15600int command::atf_unit_Start(command::atf_unit_proc& parent) {
15601    int retval = 0;
15602    if (parent.pid == 0) {
15603        verblog(atf_unit_ToCmdline(parent)); // maybe print command
15604#ifdef WIN32
15605        algo_lib::ResolveExecFname(parent.path);
15606        tempstr cmdline(atf_unit_ToCmdline(parent));
15607        parent.pid = dospawn(Zeroterm(parent.path),Zeroterm(cmdline),parent.timeout,parent.fstdin,parent.fstdout,parent.fstderr);
15608#else
15609        parent.pid = fork();
15610        if (parent.pid == 0) { // child
15611            algo_lib::DieWithParent();
15612            if (parent.timeout > 0) {
15613                alarm(parent.timeout);
15614            }
15615            if (retval==0) retval=algo_lib::ApplyRedirect(parent.fstdin , 0);
15616            if (retval==0) retval=algo_lib::ApplyRedirect(parent.fstdout, 1);
15617            if (retval==0) retval=algo_lib::ApplyRedirect(parent.fstderr, 2);
15618            if (retval==0) retval= atf_unit_Execv(parent);
15619            if (retval != 0) { // if start fails, print error
15620                int err=errno;
15621                prerr("command.atf_unit_execv"
15622                <<Keyval("errno",err)
15623                <<Keyval("errstr",strerror(err))
15624                <<Keyval("comment","Execv failed"));
15625            }
15626            _exit(127); // if failed to start, exit anyway
15627        } else if (parent.pid == -1) {
15628            retval = errno; // failed to fork
15629        }
15630#endif
15631    }
15632    parent.status = parent.pid > 0 ? 0 : -1; // if didn't start, set error status
15633    return retval;
15634}
15635
15636// --- command.atf_unit_proc.atf_unit.StartRead
15637// Start subprocess & Read output
15638algo::Fildes command::atf_unit_StartRead(command::atf_unit_proc& parent, algo_lib::FFildes &read) {
15639    int pipefd[2];
15640    int rc=pipe(pipefd);
15641    (void)rc;
15642    read.fd.value = pipefd[0];
15643    parent.fstdout  << ">&" << pipefd[1];
15644    atf_unit_Start(parent);
15645    (void)close(pipefd[1]);
15646    return read.fd;
15647}
15648
15649// --- command.atf_unit_proc.atf_unit.Kill
15650// Kill subprocess and wait
15651void command::atf_unit_Kill(command::atf_unit_proc& parent) {
15652    if (parent.pid != 0) {
15653        kill(parent.pid,9);
15654        atf_unit_Wait(parent);
15655    }
15656}
15657
15658// --- command.atf_unit_proc.atf_unit.Wait
15659// Wait for subprocess to return
15660void command::atf_unit_Wait(command::atf_unit_proc& parent) {
15661    if (parent.pid > 0) {
15662        int wait_flags = 0;
15663        int wait_status = 0;
15664        int rc = -1;
15665        do {
15666            // really wait for subprocess to exit
15667            rc = waitpid(parent.pid,&wait_status,wait_flags);
15668        } while (rc==-1 && errno==EINTR);
15669        if (rc == parent.pid) {
15670            parent.status = wait_status;
15671            parent.pid = 0;
15672        }
15673    }
15674}
15675
15676// --- command.atf_unit_proc.atf_unit.Exec
15677// Start + Wait
15678// Execute subprocess and return exit code
15679int command::atf_unit_Exec(command::atf_unit_proc& parent) {
15680    atf_unit_Start(parent);
15681    atf_unit_Wait(parent);
15682    return parent.status;
15683}
15684
15685// --- command.atf_unit_proc.atf_unit.ExecX
15686// Start + Wait, throw exception on error
15687// Execute subprocess; throw human-readable exception on error
15688void command::atf_unit_ExecX(command::atf_unit_proc& parent) {
15689    int rc = atf_unit_Exec(parent);
15690    vrfy(rc==0, tempstr() << "algo_lib.exec" << Keyval("cmd",atf_unit_ToCmdline(parent))
15691    << Keyval("comment",algo::DescribeWaitStatus(parent.status)));
15692}
15693
15694// --- command.atf_unit_proc.atf_unit.Execv
15695// Call execv()
15696// Call execv with specified parameters
15697int command::atf_unit_Execv(command::atf_unit_proc& parent) {
15698    int ret = 0;
15699    algo::StringAry args;
15700    atf_unit_ToArgv(parent, args);
15701    char **argv = (char**)alloca((ary_N(args)+1)*sizeof(*argv));
15702    ind_beg(algo::StringAry_ary_curs,arg,args) {
15703        argv[ind_curs(arg).index] = Zeroterm(arg);
15704    }ind_end;
15705    argv[ary_N(args)] = NULL;
15706    // if parent.path is relative, search for it in PATH
15707    algo_lib::ResolveExecFname(parent.path);
15708    ret = execv(Zeroterm(parent.path),argv);
15709    return ret;
15710}
15711
15712// --- command.atf_unit_proc.atf_unit.ToCmdline
15713algo::tempstr command::atf_unit_ToCmdline(command::atf_unit_proc& parent) {
15714    algo::tempstr retval;
15715    retval << parent.path << " ";
15716    command::atf_unit_PrintArgv(parent.cmd,retval);
15717    if (ch_N(parent.fstdin)) {
15718        retval << " " << parent.fstdin;
15719    }
15720    if (ch_N(parent.fstdout)) {
15721        retval << " " << parent.fstdout;
15722    }
15723    if (ch_N(parent.fstderr)) {
15724        retval << " 2" << parent.fstderr;
15725    }
15726    return retval;
15727}
15728
15729// --- command.atf_unit_proc.atf_unit.ToArgv
15730// Form array from the command line
15731void command::atf_unit_ToArgv(command::atf_unit_proc& parent, algo::StringAry& args) {
15732    ary_RemoveAll(args);
15733    ary_Alloc(args) << parent.path;
15734
15735    if (parent.cmd.unittest.expr != "%") {
15736        cstring *arg = &ary_Alloc(args);
15737        *arg << "-unittest:";
15738        command::unittest_Print(parent.cmd, *arg);
15739    }
15740
15741    if (parent.cmd.nofork != false) {
15742        cstring *arg = &ary_Alloc(args);
15743        *arg << "-nofork:";
15744        bool_Print(parent.cmd.nofork, *arg);
15745    }
15746
15747    if (parent.cmd.arg != "") {
15748        cstring *arg = &ary_Alloc(args);
15749        *arg << "-arg:";
15750        cstring_Print(parent.cmd.arg, *arg);
15751    }
15752
15753    if (parent.cmd.data_dir != "data") {
15754        cstring *arg = &ary_Alloc(args);
15755        *arg << "-data_dir:";
15756        cstring_Print(parent.cmd.data_dir, *arg);
15757    }
15758
15759    if (parent.cmd.mdbg != 0) {
15760        cstring *arg = &ary_Alloc(args);
15761        *arg << "-mdbg:";
15762        bool_Print(parent.cmd.mdbg, *arg);
15763    }
15764
15765    if (parent.cmd.perf_secs != 1.0) {
15766        cstring *arg = &ary_Alloc(args);
15767        *arg << "-perf_secs:";
15768        double_Print(parent.cmd.perf_secs, *arg);
15769    }
15770
15771    if (parent.cmd.pertest_timeout != 900) {
15772        cstring *arg = &ary_Alloc(args);
15773        *arg << "-pertest_timeout:";
15774        u32_Print(parent.cmd.pertest_timeout, *arg);
15775    }
15776
15777    if (parent.cmd.report != true) {
15778        cstring *arg = &ary_Alloc(args);
15779        *arg << "-report:";
15780        bool_Print(parent.cmd.report, *arg);
15781    }
15782
15783    if (parent.cmd.capture != false) {
15784        cstring *arg = &ary_Alloc(args);
15785        *arg << "-capture:";
15786        bool_Print(parent.cmd.capture, *arg);
15787    }
15788
15789    if (parent.cmd.check_untracked != true) {
15790        cstring *arg = &ary_Alloc(args);
15791        *arg << "-check_untracked:";
15792        bool_Print(parent.cmd.check_untracked, *arg);
15793    }
15794    for (int i=1; i < algo_lib::_db.cmdline.verbose; ++i) {
15795        ary_Alloc(args) << "-verbose";
15796    }
15797}
15798
15799// --- command.atf_unit_proc..Uninit
15800void command::atf_unit_proc_Uninit(command::atf_unit_proc& parent) {
15801    command::atf_unit_proc &row = parent; (void)row;
15802
15803    // command.atf_unit_proc.atf_unit.Uninit (Exec)  //
15804    atf_unit_Kill(parent); // kill child, ensure forward progress
15805}
15806
15807// --- command.atf_wcli.wtblacttst.Print
15808// Print back to string
15809void command::wtblacttst_Print(command::atf_wcli& parent, algo::cstring &out) {
15810    Regx_Print(parent.wtblacttst, out);
15811}
15812
15813// --- command.atf_wcli.wtblacttst.ReadStrptrMaybe
15814// Read Regx from string
15815// Convert string to field. Return success value
15816bool command::wtblacttst_ReadStrptrMaybe(command::atf_wcli& parent, algo::strptr in) {
15817    bool retval = true;
15818    Regx_ReadSql(parent.wtblacttst, in, true);
15819    return retval;
15820}
15821
15822// --- command.atf_wcli.wiki_update_fields.Addary
15823// Reserve space (this may move memory). Insert N element at the end.
15824// Return aryptr to newly inserted block.
15825// If the RHS argument aliases the array (refers to the same memory), exit program with fatal error.
15826algo::aryptr<algo::cstring> command::wiki_update_fields_Addary(command::atf_wcli& parent, algo::aryptr<algo::cstring> rhs) {
15827    bool overlaps = rhs.n_elems>0 && rhs.elems >= parent.wiki_update_fields_elems && rhs.elems < parent.wiki_update_fields_elems + parent.wiki_update_fields_max;
15828    if (UNLIKELY(overlaps)) {
15829        FatalErrorExit("command.tary_alias  field:command.atf_wcli.wiki_update_fields  comment:'alias error: sub-array is being appended to the whole'");
15830    }
15831    int nnew = rhs.n_elems;
15832    wiki_update_fields_Reserve(parent, nnew); // reserve space
15833    int at = parent.wiki_update_fields_n;
15834    for (int i = 0; i < nnew; i++) {
15835        new (parent.wiki_update_fields_elems + at + i) algo::cstring(rhs[i]);
15836        parent.wiki_update_fields_n++;
15837    }
15838    return algo::aryptr<algo::cstring>(parent.wiki_update_fields_elems + at, nnew);
15839}
15840
15841// --- command.atf_wcli.wiki_update_fields.Alloc
15842// Reserve space. Insert element at the end
15843// The new element is initialized to a default value
15844algo::cstring& command::wiki_update_fields_Alloc(command::atf_wcli& parent) {
15845    wiki_update_fields_Reserve(parent, 1);
15846    int n  = parent.wiki_update_fields_n;
15847    int at = n;
15848    algo::cstring *elems = parent.wiki_update_fields_elems;
15849    new (elems + at) algo::cstring(""); // construct new element, default initializer
15850    parent.wiki_update_fields_n = n+1;
15851    return elems[at];
15852}
15853
15854// --- command.atf_wcli.wiki_update_fields.AllocAt
15855// Reserve space for new element, reallocating the array if necessary
15856// Insert new element at specified index. Index must be in range or a fatal error occurs.
15857algo::cstring& command::wiki_update_fields_AllocAt(command::atf_wcli& parent, int at) {
15858    wiki_update_fields_Reserve(parent, 1);
15859    int n  = parent.wiki_update_fields_n;
15860    if (UNLIKELY(u64(at) >= u64(n+1))) {
15861        FatalErrorExit("command.bad_alloc_at  field:command.atf_wcli.wiki_update_fields  comment:'index out of range'");
15862    }
15863    algo::cstring *elems = parent.wiki_update_fields_elems;
15864    memmove(elems + at + 1, elems + at, (n - at) * sizeof(algo::cstring));
15865    new (elems + at) algo::cstring(""); // construct element, default initializer
15866    parent.wiki_update_fields_n = n+1;
15867    return elems[at];
15868}
15869
15870// --- command.atf_wcli.wiki_update_fields.AllocN
15871// Reserve space. Insert N elements at the end of the array, return pointer to array
15872algo::aryptr<algo::cstring> command::wiki_update_fields_AllocN(command::atf_wcli& parent, int n_elems) {
15873    wiki_update_fields_Reserve(parent, n_elems);
15874    int old_n  = parent.wiki_update_fields_n;
15875    int new_n = old_n + n_elems;
15876    algo::cstring *elems = parent.wiki_update_fields_elems;
15877    for (int i = old_n; i < new_n; i++) {
15878        new (elems + i) algo::cstring(""); // construct new element, default initialize
15879    }
15880    parent.wiki_update_fields_n = new_n;
15881    return algo::aryptr<algo::cstring>(elems + old_n, n_elems);
15882}
15883
15884// --- command.atf_wcli.wiki_update_fields.Remove
15885// Remove item by index. If index outside of range, do nothing.
15886void command::wiki_update_fields_Remove(command::atf_wcli& parent, u32 i) {
15887    u32 lim = parent.wiki_update_fields_n;
15888    algo::cstring *elems = parent.wiki_update_fields_elems;
15889    if (i < lim) {
15890        elems[i].~cstring(); // destroy element
15891        memmove(elems + i, elems + (i + 1), sizeof(algo::cstring) * (lim - (i + 1)));
15892        parent.wiki_update_fields_n = lim - 1;
15893    }
15894}
15895
15896// --- command.atf_wcli.wiki_update_fields.RemoveAll
15897void command::wiki_update_fields_RemoveAll(command::atf_wcli& parent) {
15898    u32 n = parent.wiki_update_fields_n;
15899    while (n > 0) {
15900        n -= 1;
15901        parent.wiki_update_fields_elems[n].~cstring();
15902        parent.wiki_update_fields_n = n;
15903    }
15904}
15905
15906// --- command.atf_wcli.wiki_update_fields.RemoveLast
15907// Delete last element of array. Do nothing if array is empty.
15908void command::wiki_update_fields_RemoveLast(command::atf_wcli& parent) {
15909    u64 n = parent.wiki_update_fields_n;
15910    if (n > 0) {
15911        n -= 1;
15912        wiki_update_fields_qFind(parent, u64(n)).~cstring();
15913        parent.wiki_update_fields_n = n;
15914    }
15915}
15916
15917// --- command.atf_wcli.wiki_update_fields.AbsReserve
15918// Make sure N elements fit in array. Process dies if out of memory
15919void command::wiki_update_fields_AbsReserve(command::atf_wcli& parent, int n) {
15920    u32 old_max  = parent.wiki_update_fields_max;
15921    if (n > i32(old_max)) {
15922        u32 new_max  = i32_Max(i32_Max(old_max * 2, n), 4);
15923        void *new_mem = algo_lib::malloc_ReallocMem(parent.wiki_update_fields_elems, old_max * sizeof(algo::cstring), new_max * sizeof(algo::cstring));
15924        if (UNLIKELY(!new_mem)) {
15925            FatalErrorExit("command.tary_nomem  field:command.atf_wcli.wiki_update_fields  comment:'out of memory'");
15926        }
15927        parent.wiki_update_fields_elems = (algo::cstring*)new_mem;
15928        parent.wiki_update_fields_max = new_max;
15929    }
15930}
15931
15932// --- command.atf_wcli.wiki_update_fields.Setary
15933// Copy contents of RHS to PARENT.
15934void command::wiki_update_fields_Setary(command::atf_wcli& parent, command::atf_wcli &rhs) {
15935    wiki_update_fields_RemoveAll(parent);
15936    int nnew = rhs.wiki_update_fields_n;
15937    wiki_update_fields_Reserve(parent, nnew); // reserve space
15938    for (int i = 0; i < nnew; i++) { // copy elements over
15939        new (parent.wiki_update_fields_elems + i) algo::cstring(wiki_update_fields_qFind(rhs, i));
15940        parent.wiki_update_fields_n = i + 1;
15941    }
15942}
15943
15944// --- command.atf_wcli.wiki_update_fields.Setary2
15945// Copy specified array into wiki_update_fields, discarding previous contents.
15946// If the RHS argument aliases the array (refers to the same memory), throw exception.
15947void command::wiki_update_fields_Setary(command::atf_wcli& parent, const algo::aryptr<algo::cstring> &rhs) {
15948    wiki_update_fields_RemoveAll(parent);
15949    wiki_update_fields_Addary(parent, rhs);
15950}
15951
15952// --- command.atf_wcli.wiki_update_fields.AllocNVal
15953// Reserve space. Insert N elements at the end of the array, return pointer to array
15954algo::aryptr<algo::cstring> command::wiki_update_fields_AllocNVal(command::atf_wcli& parent, int n_elems, const algo::cstring& val) {
15955    wiki_update_fields_Reserve(parent, n_elems);
15956    int old_n  = parent.wiki_update_fields_n;
15957    int new_n = old_n + n_elems;
15958    algo::cstring *elems = parent.wiki_update_fields_elems;
15959    for (int i = old_n; i < new_n; i++) {
15960        new (elems + i) algo::cstring(val);
15961    }
15962    parent.wiki_update_fields_n = new_n;
15963    return algo::aryptr<algo::cstring>(elems + old_n, n_elems);
15964}
15965
15966// --- command.atf_wcli.wiki_update_fields.ReadStrptrMaybe
15967// A single element is read from input string and appended to the array.
15968// If the string contains an error, the array is untouched.
15969// Function returns success value.
15970bool command::wiki_update_fields_ReadStrptrMaybe(command::atf_wcli& parent, algo::strptr in_str) {
15971    bool retval = true;
15972    algo::cstring &elem = wiki_update_fields_Alloc(parent);
15973    retval = algo::cstring_ReadStrptrMaybe(elem, in_str);
15974    if (!retval) {
15975        wiki_update_fields_RemoveLast(parent);
15976    }
15977    return retval;
15978}
15979
15980// --- command.atf_wcli..ReadFieldMaybe
15981bool command::atf_wcli_ReadFieldMaybe(command::atf_wcli& parent, algo::strptr field, algo::strptr strval) {
15982    bool retval = true;
15983    command::FieldId field_id;
15984    (void)value_SetStrptrMaybe(field_id,algo::Pathcomp(field, ".LL"));
15985    switch(field_id) {
15986        case command_FieldId_capture: {
15987            retval = bool_ReadStrptrMaybe(parent.capture, strval);
15988            break;
15989        }
15990        case command_FieldId_wtblacttst: {
15991            retval = wtblacttst_ReadStrptrMaybe(parent, strval);
15992            break;
15993        }
15994        case command_FieldId_in: {
15995            retval = algo::cstring_ReadStrptrMaybe(parent.in, strval);
15996            break;
15997        }
15998        case command_FieldId_wiki_envnode: {
15999            retval = algo::Smallstr50_ReadStrptrMaybe(parent.wiki_envnode, strval);
16000            break;
16001        }
16002        case command_FieldId_wiki_kill: {
16003            retval = bool_ReadStrptrMaybe(parent.wiki_kill, strval);
16004            break;
16005        }
16006        case command_FieldId_wiki_start: {
16007            retval = bool_ReadStrptrMaybe(parent.wiki_start, strval);
16008            break;
16009        }
16010        case command_FieldId_wiki_update: {
16011            retval = bool_ReadStrptrMaybe(parent.wiki_update, strval);
16012            break;
16013        }
16014        case command_FieldId_wiki_update_help: {
16015            retval = bool_ReadStrptrMaybe(parent.wiki_update_help, strval);
16016            break;
16017        }
16018        case command_FieldId_wiki_update_fields: {
16019            retval = wiki_update_fields_ReadStrptrMaybe(parent, strval);
16020            break;
16021        }
16022        case command_FieldId_wiki_clean_start: {
16023            retval = bool_ReadStrptrMaybe(parent.wiki_clean_start, strval);
16024            break;
16025        }
16026        case command_FieldId_authdir_dflt: {
16027            retval = bool_ReadStrptrMaybe(parent.authdir_dflt, strval);
16028            break;
16029        }
16030        case command_FieldId_dry_run: {
16031            retval = bool_ReadStrptrMaybe(parent.dry_run, strval);
16032            break;
16033        }
16034        case command_FieldId_wikisite: {
16035            retval = algo::Smallstr50_ReadStrptrMaybe(parent.wikisite, strval);
16036            break;
16037        }
16038        default: break;
16039    }
16040    if (!retval) {
16041        algo_lib::AppendErrtext("attr",field);
16042    }
16043    return retval;
16044}
16045
16046// --- command.atf_wcli..ReadTupleMaybe
16047// Read fields of command::atf_wcli from attributes of ascii tuple TUPLE
16048bool command::atf_wcli_ReadTupleMaybe(command::atf_wcli &parent, algo::Tuple &tuple) {
16049    bool retval = true;
16050    int anon_idx = 0;
16051    ind_beg(algo::Tuple_attrs_curs,attr,tuple) {
16052        if (ch_N(attr.name) == 0) {
16053            attr.name = atf_wcli_GetAnon(parent, anon_idx++);
16054        }
16055        retval = atf_wcli_ReadFieldMaybe(parent, attr.name, attr.value);
16056        if (!retval) {
16057            break;
16058        }
16059    }ind_end;
16060    return retval;
16061}
16062
16063// --- command.atf_wcli..Init
16064// Set all fields to initial values.
16065void command::atf_wcli_Init(command::atf_wcli& parent) {
16066    parent.capture = bool(false);
16067    Regx_ReadSql(parent.wtblacttst, "", true);
16068    parent.in = algo::strptr("data");
16069    parent.wiki_envnode = algo::strptr("dev.wiki-qa1-nyc0");
16070    parent.wiki_kill = bool(false);
16071    parent.wiki_start = bool(false);
16072    parent.wiki_update = bool(false);
16073    parent.wiki_update_help = bool(false);
16074    parent.wiki_update_fields_elems 	= 0; // (command.atf_wcli.wiki_update_fields)
16075    parent.wiki_update_fields_n     	= 0; // (command.atf_wcli.wiki_update_fields)
16076    parent.wiki_update_fields_max   	= 0; // (command.atf_wcli.wiki_update_fields)
16077    parent.wiki_clean_start = bool(false);
16078    parent.authdir_dflt = bool(false);
16079    parent.dry_run = bool(false);
16080    parent.wikisite = algo::strptr("Openacr");
16081}
16082
16083// --- command.atf_wcli..Uninit
16084void command::atf_wcli_Uninit(command::atf_wcli& parent) {
16085    command::atf_wcli &row = parent; (void)row;
16086
16087    // command.atf_wcli.wiki_update_fields.Uninit (Tary)  //additional key:value pairs for use with -wiki_update, see -wiki_update_help
16088    // remove all elements from command.atf_wcli.wiki_update_fields
16089    wiki_update_fields_RemoveAll(parent);
16090    // free memory for Tary command.atf_wcli.wiki_update_fields
16091    algo_lib::malloc_FreeMem(parent.wiki_update_fields_elems, sizeof(algo::cstring)*parent.wiki_update_fields_max); // (command.atf_wcli.wiki_update_fields)
16092}
16093
16094// --- command.atf_wcli..ToCmdline
16095// Convenience function that returns a full command line
16096// Assume command is in a directory called bin
16097tempstr command::atf_wcli_ToCmdline(command::atf_wcli& row) {
16098    tempstr ret;
16099    ret << "bin/atf_wcli ";
16100    atf_wcli_PrintArgv(row, ret);
16101    // inherit less intense verbose, debug options
16102    for (int i = 1; i < algo_lib::_db.cmdline.verbose; i++) {
16103        ret << " -verbose";
16104    }
16105    for (int i = 1; i < algo_lib::_db.cmdline.debug; i++) {
16106        ret << " -debug";
16107    }
16108    return ret;
16109}
16110
16111// --- command.atf_wcli..PrintArgv
16112// print string representation of ROW to string STR
16113// cfmt:command.atf_wcli.Argv  printfmt:Tuple
16114void command::atf_wcli_PrintArgv(command::atf_wcli& row, algo::cstring& str) {
16115    algo::tempstr temp;
16116    (void)temp;
16117    (void)str;
16118    if (!(row.capture == false)) {
16119        ch_RemoveAll(temp);
16120        bool_Print(row.capture, temp);
16121        str << " -capture:";
16122        strptr_PrintBash(temp,str);
16123    }
16124    if (!(row.wtblacttst.expr == "")) {
16125        ch_RemoveAll(temp);
16126        command::wtblacttst_Print(const_cast<command::atf_wcli&>(row), temp);
16127        str << " -wtblacttst:";
16128        strptr_PrintBash(temp,str);
16129    }
16130    if (!(row.in == "data")) {
16131        ch_RemoveAll(temp);
16132        cstring_Print(row.in, temp);
16133        str << " -in:";
16134        strptr_PrintBash(temp,str);
16135    }
16136    if (!(row.wiki_envnode == "dev.wiki-qa1-nyc0")) {
16137        ch_RemoveAll(temp);
16138        Smallstr50_Print(row.wiki_envnode, temp);
16139        str << " -wiki_envnode:";
16140        strptr_PrintBash(temp,str);
16141    }
16142    if (!(row.wiki_kill == false)) {
16143        ch_RemoveAll(temp);
16144        bool_Print(row.wiki_kill, temp);
16145        str << " -wiki_kill:";
16146        strptr_PrintBash(temp,str);
16147    }
16148    if (!(row.wiki_start == false)) {
16149        ch_RemoveAll(temp);
16150        bool_Print(row.wiki_start, temp);
16151        str << " -wiki_start:";
16152        strptr_PrintBash(temp,str);
16153    }
16154    if (!(row.wiki_update == false)) {
16155        ch_RemoveAll(temp);
16156        bool_Print(row.wiki_update, temp);
16157        str << " -wiki_update:";
16158        strptr_PrintBash(temp,str);
16159    }
16160    if (!(row.wiki_update_help == false)) {
16161        ch_RemoveAll(temp);
16162        bool_Print(row.wiki_update_help, temp);
16163        str << " -wiki_update_help:";
16164        strptr_PrintBash(temp,str);
16165    }
16166    ind_beg(atf_wcli_wiki_update_fields_curs,value,row) {
16167        ch_RemoveAll(temp);
16168        cstring_Print(value, temp);
16169        str << " -wiki_update_fields:";
16170        strptr_PrintBash(temp,str);
16171    }ind_end;
16172    if (!(row.wiki_clean_start == false)) {
16173        ch_RemoveAll(temp);
16174        bool_Print(row.wiki_clean_start, temp);
16175        str << " -wiki_clean_start:";
16176        strptr_PrintBash(temp,str);
16177    }
16178    if (!(row.authdir_dflt == false)) {
16179        ch_RemoveAll(temp);
16180        bool_Print(row.authdir_dflt, temp);
16181        str << " -authdir_dflt:";
16182        strptr_PrintBash(temp,str);
16183    }
16184    if (!(row.dry_run == false)) {
16185        ch_RemoveAll(temp);
16186        bool_Print(row.dry_run, temp);
16187        str << " -dry_run:";
16188        strptr_PrintBash(temp,str);
16189    }
16190    if (!(row.wikisite == "Openacr")) {
16191        ch_RemoveAll(temp);
16192        Smallstr50_Print(row.wikisite, temp);
16193        str << " -wikisite:";
16194        strptr_PrintBash(temp,str);
16195    }
16196}
16197
16198// --- command.atf_wcli..GetAnon
16199algo::strptr command::atf_wcli_GetAnon(command::atf_wcli &parent, i32 idx) {
16200    (void)parent;//only to avoid -Wunused-parameter
16201    switch(idx) {
16202        default: return strptr("wiki_update_fields", 18);
16203    }
16204}
16205
16206// --- command.atf_wcli..NArgs
16207// Used with command lines
16208// Return # of command-line arguments that must follow this argument
16209// If FIELD is invalid, return -1
16210i32 command::atf_wcli_NArgs(command::FieldId field, algo::strptr& out_dflt, bool* out_anon) {
16211    i32 retval = 1;
16212    switch (field) {
16213        case command_FieldId_capture: { // $comment
16214            *out_anon = false;
16215            retval=0;
16216            out_dflt="Y";
16217        } break;
16218        case command_FieldId_wtblacttst: { // bool: no argument required but value may be specified as capture:Y
16219            *out_anon = false;
16220        } break;
16221        case command_FieldId_in: { // bool: no argument required but value may be specified as capture:Y
16222            *out_anon = false;
16223        } break;
16224        case command_FieldId_wiki_envnode: { // bool: no argument required but value may be specified as capture:Y
16225            *out_anon = false;
16226        } break;
16227        case command_FieldId_wiki_kill: { // bool: no argument required but value may be specified as capture:Y
16228            *out_anon = false;
16229            retval=0;
16230            out_dflt="Y";
16231        } break;
16232        case command_FieldId_wiki_start: { // bool: no argument required but value may be specified as wiki_kill:Y
16233            *out_anon = false;
16234            retval=0;
16235            out_dflt="Y";
16236        } break;
16237        case command_FieldId_wiki_update: { // bool: no argument required but value may be specified as wiki_start:Y
16238            *out_anon = false;
16239            retval=0;
16240            out_dflt="Y";
16241        } break;
16242        case command_FieldId_wiki_update_help: { // bool: no argument required but value may be specified as wiki_update:Y
16243            *out_anon = false;
16244            retval=0;
16245            out_dflt="Y";
16246        } break;
16247        case command_FieldId_wiki_update_fields: { // bool: no argument required but value may be specified as wiki_update_help:Y
16248            *out_anon = true;
16249        } break;
16250        case command_FieldId_wiki_clean_start: { // bool: no argument required but value may be specified as wiki_update_help:Y
16251            *out_anon = false;
16252            retval=0;
16253            out_dflt="Y";
16254        } break;
16255        case command_FieldId_authdir_dflt: { // bool: no argument required but value may be specified as wiki_clean_start:Y
16256            *out_anon = false;
16257            retval=0;
16258            out_dflt="Y";
16259        } break;
16260        case command_FieldId_dry_run: { // bool: no argument required but value may be specified as authdir_dflt:Y
16261            *out_anon = false;
16262            retval=0;
16263            out_dflt="Y";
16264        } break;
16265        case command_FieldId_wikisite: { // bool: no argument required but value may be specified as dry_run:Y
16266            *out_anon = false;
16267        } break;
16268        default:
16269        retval=-1; // unrecognized
16270    }
16271    return retval;
16272}
16273
16274// --- command.atf_wcli_proc.atf_wcli.Start
16275// Start subprocess
16276// If subprocess already running, do nothing. Otherwise, start it
16277int command::atf_wcli_Start(command::atf_wcli_proc& parent) {
16278    int retval = 0;
16279    if (parent.pid == 0) {
16280        verblog(atf_wcli_ToCmdline(parent)); // maybe print command
16281#ifdef WIN32
16282        algo_lib::ResolveExecFname(parent.path);
16283        tempstr cmdline(atf_wcli_ToCmdline(parent));
16284        parent.pid = dospawn(Zeroterm(parent.path),Zeroterm(cmdline),parent.timeout,parent.fstdin,parent.fstdout,parent.fstderr);
16285#else
16286        parent.pid = fork();
16287        if (parent.pid == 0) { // child
16288            algo_lib::DieWithParent();
16289            if (parent.timeout > 0) {
16290                alarm(parent.timeout);
16291            }
16292            if (retval==0) retval=algo_lib::ApplyRedirect(parent.fstdin , 0);
16293            if (retval==0) retval=algo_lib::ApplyRedirect(parent.fstdout, 1);
16294            if (retval==0) retval=algo_lib::ApplyRedirect(parent.fstderr, 2);
16295            if (retval==0) retval= atf_wcli_Execv(parent);
16296            if (retval != 0) { // if start fails, print error
16297                int err=errno;
16298                prerr("command.atf_wcli_execv"
16299                <<Keyval("errno",err)
16300                <<Keyval("errstr",strerror(err))
16301                <<Keyval("comment","Execv failed"));
16302            }
16303            _exit(127); // if failed to start, exit anyway
16304        } else if (parent.pid == -1) {
16305            retval = errno; // failed to fork
16306        }
16307#endif
16308    }
16309    parent.status = parent.pid > 0 ? 0 : -1; // if didn't start, set error status
16310    return retval;
16311}
16312
16313// --- command.atf_wcli_proc.atf_wcli.StartRead
16314// Start subprocess & Read output
16315algo::Fildes command::atf_wcli_StartRead(command::atf_wcli_proc& parent, algo_lib::FFildes &read) {
16316    int pipefd[2];
16317    int rc=pipe(pipefd);
16318    (void)rc;
16319    read.fd.value = pipefd[0];
16320    parent.fstdout  << ">&" << pipefd[1];
16321    atf_wcli_Start(parent);
16322    (void)close(pipefd[1]);
16323    return read.fd;
16324}
16325
16326// --- command.atf_wcli_proc.atf_wcli.Kill
16327// Kill subprocess and wait
16328void command::atf_wcli_Kill(command::atf_wcli_proc& parent) {
16329    if (parent.pid != 0) {
16330        kill(parent.pid,9);
16331        atf_wcli_Wait(parent);
16332    }
16333}
16334
16335// --- command.atf_wcli_proc.atf_wcli.Wait
16336// Wait for subprocess to return
16337void command::atf_wcli_Wait(command::atf_wcli_proc& parent) {
16338    if (parent.pid > 0) {
16339        int wait_flags = 0;
16340        int wait_status = 0;
16341        int rc = -1;
16342        do {
16343            // really wait for subprocess to exit
16344            rc = waitpid(parent.pid,&wait_status,wait_flags);
16345        } while (rc==-1 && errno==EINTR);
16346        if (rc == parent.pid) {
16347            parent.status = wait_status;
16348            parent.pid = 0;
16349        }
16350    }
16351}
16352
16353// --- command.atf_wcli_proc.atf_wcli.Exec
16354// Start + Wait
16355// Execute subprocess and return exit code
16356int command::atf_wcli_Exec(command::atf_wcli_proc& parent) {
16357    atf_wcli_Start(parent);
16358    atf_wcli_Wait(parent);
16359    return parent.status;
16360}
16361
16362// --- command.atf_wcli_proc.atf_wcli.ExecX
16363// Start + Wait, throw exception on error
16364// Execute subprocess; throw human-readable exception on error
16365void command::atf_wcli_ExecX(command::atf_wcli_proc& parent) {
16366    int rc = atf_wcli_Exec(parent);
16367    vrfy(rc==0, tempstr() << "algo_lib.exec" << Keyval("cmd",atf_wcli_ToCmdline(parent))
16368    << Keyval("comment",algo::DescribeWaitStatus(parent.status)));
16369}
16370
16371// --- command.atf_wcli_proc.atf_wcli.Execv
16372// Call execv()
16373// Call execv with specified parameters
16374int command::atf_wcli_Execv(command::atf_wcli_proc& parent) {
16375    int ret = 0;
16376    algo::StringAry args;
16377    atf_wcli_ToArgv(parent, args);
16378    char **argv = (char**)alloca((ary_N(args)+1)*sizeof(*argv));
16379    ind_beg(algo::StringAry_ary_curs,arg,args) {
16380        argv[ind_curs(arg).index] = Zeroterm(arg);
16381    }ind_end;
16382    argv[ary_N(args)] = NULL;
16383    // if parent.path is relative, search for it in PATH
16384    algo_lib::ResolveExecFname(parent.path);
16385    ret = execv(Zeroterm(parent.path),argv);
16386    return ret;
16387}
16388
16389// --- command.atf_wcli_proc.atf_wcli.ToCmdline
16390algo::tempstr command::atf_wcli_ToCmdline(command::atf_wcli_proc& parent) {
16391    algo::tempstr retval;
16392    retval << parent.path << " ";
16393    command::atf_wcli_PrintArgv(parent.cmd,retval);
16394    if (ch_N(parent.fstdin)) {
16395        retval << " " << parent.fstdin;
16396    }
16397    if (ch_N(parent.fstdout)) {
16398        retval << " " << parent.fstdout;
16399    }
16400    if (ch_N(parent.fstderr)) {
16401        retval << " 2" << parent.fstderr;
16402    }
16403    return retval;
16404}
16405
16406// --- command.atf_wcli_proc.atf_wcli.ToArgv
16407// Form array from the command line
16408void command::atf_wcli_ToArgv(command::atf_wcli_proc& parent, algo::StringAry& args) {
16409    ary_RemoveAll(args);
16410    ary_Alloc(args) << parent.path;
16411
16412    if (parent.cmd.capture != false) {
16413        cstring *arg = &ary_Alloc(args);
16414        *arg << "-capture:";
16415        bool_Print(parent.cmd.capture, *arg);
16416    }
16417
16418    if (parent.cmd.wtblacttst.expr != "") {
16419        cstring *arg = &ary_Alloc(args);
16420        *arg << "-wtblacttst:";
16421        command::wtblacttst_Print(parent.cmd, *arg);
16422    }
16423
16424    if (parent.cmd.in != "data") {
16425        cstring *arg = &ary_Alloc(args);
16426        *arg << "-in:";
16427        cstring_Print(parent.cmd.in, *arg);
16428    }
16429
16430    if (parent.cmd.wiki_envnode != "dev.wiki-qa1-nyc0") {
16431        cstring *arg = &ary_Alloc(args);
16432        *arg << "-wiki_envnode:";
16433        Smallstr50_Print(parent.cmd.wiki_envnode, *arg);
16434    }
16435
16436    if (parent.cmd.wiki_kill != false) {
16437        cstring *arg = &ary_Alloc(args);
16438        *arg << "-wiki_kill:";
16439        bool_Print(parent.cmd.wiki_kill, *arg);
16440    }
16441
16442    if (parent.cmd.wiki_start != false) {
16443        cstring *arg = &ary_Alloc(args);
16444        *arg << "-wiki_start:";
16445        bool_Print(parent.cmd.wiki_start, *arg);
16446    }
16447
16448    if (parent.cmd.wiki_update != false) {
16449        cstring *arg = &ary_Alloc(args);
16450        *arg << "-wiki_update:";
16451        bool_Print(parent.cmd.wiki_update, *arg);
16452    }
16453
16454    if (parent.cmd.wiki_update_help != false) {
16455        cstring *arg = &ary_Alloc(args);
16456        *arg << "-wiki_update_help:";
16457        bool_Print(parent.cmd.wiki_update_help, *arg);
16458    }
16459    ind_beg(command::atf_wcli_wiki_update_fields_curs,value,parent.cmd) {
16460        cstring *arg = &ary_Alloc(args);
16461        *arg << "-wiki_update_fields:";
16462        cstring_Print(value, *arg);
16463    }ind_end;
16464
16465    if (parent.cmd.wiki_clean_start != false) {
16466        cstring *arg = &ary_Alloc(args);
16467        *arg << "-wiki_clean_start:";
16468        bool_Print(parent.cmd.wiki_clean_start, *arg);
16469    }
16470
16471    if (parent.cmd.authdir_dflt != false) {
16472        cstring *arg = &ary_Alloc(args);
16473        *arg << "-authdir_dflt:";
16474        bool_Print(parent.cmd.authdir_dflt, *arg);
16475    }
16476
16477    if (parent.cmd.dry_run != false) {
16478        cstring *arg = &ary_Alloc(args);
16479        *arg << "-dry_run:";
16480        bool_Print(parent.cmd.dry_run, *arg);
16481    }
16482
16483    if (parent.cmd.wikisite != "Openacr") {
16484        cstring *arg = &ary_Alloc(args);
16485        *arg << "-wikisite:";
16486        Smallstr50_Print(parent.cmd.wikisite, *arg);
16487    }
16488    for (int i=1; i < algo_lib::_db.cmdline.verbose; ++i) {
16489        ary_Alloc(args) << "-verbose";
16490    }
16491}
16492
16493// --- command.atf_wcli_proc..Uninit
16494void command::atf_wcli_proc_Uninit(command::atf_wcli_proc& parent) {
16495    command::atf_wcli_proc &row = parent; (void)row;
16496
16497    // command.atf_wcli_proc.atf_wcli.Uninit (Exec)  //
16498    atf_wcli_Kill(parent); // kill child, ensure forward progress
16499}
16500
16501// --- command.atf_ws..ReadFieldMaybe
16502bool command::atf_ws_ReadFieldMaybe(command::atf_ws& parent, algo::strptr field, algo::strptr strval) {
16503    bool retval = true;
16504    command::FieldId field_id;
16505    (void)value_SetStrptrMaybe(field_id,field);
16506    switch(field_id) {
16507        case command_FieldId_in: {
16508            retval = algo::cstring_ReadStrptrMaybe(parent.in, strval);
16509            break;
16510        }
16511        case command_FieldId_randomize_ports: {
16512            retval = bool_ReadStrptrMaybe(parent.randomize_ports, strval);
16513            break;
16514        }
16515        default: break;
16516    }
16517    if (!retval) {
16518        algo_lib::AppendErrtext("attr",field);
16519    }
16520    return retval;
16521}
16522
16523// --- command.atf_ws..ReadTupleMaybe
16524// Read fields of command::atf_ws from attributes of ascii tuple TUPLE
16525bool command::atf_ws_ReadTupleMaybe(command::atf_ws &parent, algo::Tuple &tuple) {
16526    bool retval = true;
16527    ind_beg(algo::Tuple_attrs_curs,attr,tuple) {
16528        retval = atf_ws_ReadFieldMaybe(parent, attr.name, attr.value);
16529        if (!retval) {
16530            break;
16531        }
16532    }ind_end;
16533    return retval;
16534}
16535
16536// --- command.atf_ws..ToCmdline
16537// Convenience function that returns a full command line
16538// Assume command is in a directory called bin
16539tempstr command::atf_ws_ToCmdline(command::atf_ws& row) {
16540    tempstr ret;
16541    ret << "bin/atf_ws ";
16542    atf_ws_PrintArgv(row, ret);
16543    // inherit less intense verbose, debug options
16544    for (int i = 1; i < algo_lib::_db.cmdline.verbose; i++) {
16545        ret << " -verbose";
16546    }
16547    for (int i = 1; i < algo_lib::_db.cmdline.debug; i++) {
16548        ret << " -debug";
16549    }
16550    return ret;
16551}
16552
16553// --- command.atf_ws..PrintArgv
16554// print string representation of ROW to string STR
16555// cfmt:command.atf_ws.Argv  printfmt:Tuple
16556void command::atf_ws_PrintArgv(command::atf_ws& row, algo::cstring& str) {
16557    algo::tempstr temp;
16558    (void)temp;
16559    (void)str;
16560    if (!(row.in == "data")) {
16561        ch_RemoveAll(temp);
16562        cstring_Print(row.in, temp);
16563        str << " -in:";
16564        strptr_PrintBash(temp,str);
16565    }
16566    if (!(row.randomize_ports == true)) {
16567        ch_RemoveAll(temp);
16568        bool_Print(row.randomize_ports, temp);
16569        str << " -randomize_ports:";
16570        strptr_PrintBash(temp,str);
16571    }
16572}
16573
16574// --- command.atf_ws..NArgs
16575// Used with command lines
16576// Return # of command-line arguments that must follow this argument
16577// If FIELD is invalid, return -1
16578i32 command::atf_ws_NArgs(command::FieldId field, algo::strptr& out_dflt, bool* out_anon) {
16579    i32 retval = 1;
16580    switch (field) {
16581        case command_FieldId_in: { // $comment
16582            *out_anon = false;
16583        } break;
16584        case command_FieldId_randomize_ports: { // $comment
16585            *out_anon = false;
16586            retval=0;
16587            out_dflt="Y";
16588        } break;
16589        default:
16590        retval=-1; // unrecognized
16591    }
16592    return retval;
16593}
16594
16595// --- command.atf_ws_proc.atf_ws.Start
16596// Start subprocess
16597// If subprocess already running, do nothing. Otherwise, start it
16598int command::atf_ws_Start(command::atf_ws_proc& parent) {
16599    int retval = 0;
16600    if (parent.pid == 0) {
16601        verblog(atf_ws_ToCmdline(parent)); // maybe print command
16602#ifdef WIN32
16603        algo_lib::ResolveExecFname(parent.path);
16604        tempstr cmdline(atf_ws_ToCmdline(parent));
16605        parent.pid = dospawn(Zeroterm(parent.path),Zeroterm(cmdline),parent.timeout,parent.fstdin,parent.fstdout,parent.fstderr);
16606#else
16607        parent.pid = fork();
16608        if (parent.pid == 0) { // child
16609            algo_lib::DieWithParent();
16610            if (parent.timeout > 0) {
16611                alarm(parent.timeout);
16612            }
16613            if (retval==0) retval=algo_lib::ApplyRedirect(parent.fstdin , 0);
16614            if (retval==0) retval=algo_lib::ApplyRedirect(parent.fstdout, 1);
16615            if (retval==0) retval=algo_lib::ApplyRedirect(parent.fstderr, 2);
16616            if (retval==0) retval= atf_ws_Execv(parent);
16617            if (retval != 0) { // if start fails, print error
16618                int err=errno;
16619                prerr("command.atf_ws_execv"
16620                <<Keyval("errno",err)
16621                <<Keyval("errstr",strerror(err))
16622                <<Keyval("comment","Execv failed"));
16623            }
16624            _exit(127); // if failed to start, exit anyway
16625        } else if (parent.pid == -1) {
16626            retval = errno; // failed to fork
16627        }
16628#endif
16629    }
16630    parent.status = parent.pid > 0 ? 0 : -1; // if didn't start, set error status
16631    return retval;
16632}
16633
16634// --- command.atf_ws_proc.atf_ws.StartRead
16635// Start subprocess & Read output
16636algo::Fildes command::atf_ws_StartRead(command::atf_ws_proc& parent, algo_lib::FFildes &read) {
16637    int pipefd[2];
16638    int rc=pipe(pipefd);
16639    (void)rc;
16640    read.fd.value = pipefd[0];
16641    parent.fstdout  << ">&" << pipefd[1];
16642    atf_ws_Start(parent);
16643    (void)close(pipefd[1]);
16644    return read.fd;
16645}
16646
16647// --- command.atf_ws_proc.atf_ws.Kill
16648// Kill subprocess and wait
16649void command::atf_ws_Kill(command::atf_ws_proc& parent) {
16650    if (parent.pid != 0) {
16651        kill(parent.pid,9);
16652        atf_ws_Wait(parent);
16653    }
16654}
16655
16656// --- command.atf_ws_proc.atf_ws.Wait
16657// Wait for subprocess to return
16658void command::atf_ws_Wait(command::atf_ws_proc& parent) {
16659    if (parent.pid > 0) {
16660        int wait_flags = 0;
16661        int wait_status = 0;
16662        int rc = -1;
16663        do {
16664            // really wait for subprocess to exit
16665            rc = waitpid(parent.pid,&wait_status,wait_flags);
16666        } while (rc==-1 && errno==EINTR);
16667        if (rc == parent.pid) {
16668            parent.status = wait_status;
16669            parent.pid = 0;
16670        }
16671    }
16672}
16673
16674// --- command.atf_ws_proc.atf_ws.Exec
16675// Start + Wait
16676// Execute subprocess and return exit code
16677int command::atf_ws_Exec(command::atf_ws_proc& parent) {
16678    atf_ws_Start(parent);
16679    atf_ws_Wait(parent);
16680    return parent.status;
16681}
16682
16683// --- command.atf_ws_proc.atf_ws.ExecX
16684// Start + Wait, throw exception on error
16685// Execute subprocess; throw human-readable exception on error
16686void command::atf_ws_ExecX(command::atf_ws_proc& parent) {
16687    int rc = atf_ws_Exec(parent);
16688    vrfy(rc==0, tempstr() << "algo_lib.exec" << Keyval("cmd",atf_ws_ToCmdline(parent))
16689    << Keyval("comment",algo::DescribeWaitStatus(parent.status)));
16690}
16691
16692// --- command.atf_ws_proc.atf_ws.Execv
16693// Call execv()
16694// Call execv with specified parameters
16695int command::atf_ws_Execv(command::atf_ws_proc& parent) {
16696    int ret = 0;
16697    algo::StringAry args;
16698    atf_ws_ToArgv(parent, args);
16699    char **argv = (char**)alloca((ary_N(args)+1)*sizeof(*argv));
16700    ind_beg(algo::StringAry_ary_curs,arg,args) {
16701        argv[ind_curs(arg).index] = Zeroterm(arg);
16702    }ind_end;
16703    argv[ary_N(args)] = NULL;
16704    // if parent.path is relative, search for it in PATH
16705    algo_lib::ResolveExecFname(parent.path);
16706    ret = execv(Zeroterm(parent.path),argv);
16707    return ret;
16708}
16709
16710// --- command.atf_ws_proc.atf_ws.ToCmdline
16711algo::tempstr command::atf_ws_ToCmdline(command::atf_ws_proc& parent) {
16712    algo::tempstr retval;
16713    retval << parent.path << " ";
16714    command::atf_ws_PrintArgv(parent.cmd,retval);
16715    if (ch_N(parent.fstdin)) {
16716        retval << " " << parent.fstdin;
16717    }
16718    if (ch_N(parent.fstdout)) {
16719        retval << " " << parent.fstdout;
16720    }
16721    if (ch_N(parent.fstderr)) {
16722        retval << " 2" << parent.fstderr;
16723    }
16724    return retval;
16725}
16726
16727// --- command.atf_ws_proc.atf_ws.ToArgv
16728// Form array from the command line
16729void command::atf_ws_ToArgv(command::atf_ws_proc& parent, algo::StringAry& args) {
16730    ary_RemoveAll(args);
16731    ary_Alloc(args) << parent.path;
16732
16733    if (parent.cmd.in != "data") {
16734        cstring *arg = &ary_Alloc(args);
16735        *arg << "-in:";
16736        cstring_Print(parent.cmd.in, *arg);
16737    }
16738
16739    if (parent.cmd.randomize_ports != true) {
16740        cstring *arg = &ary_Alloc(args);
16741        *arg << "-randomize_ports:";
16742        bool_Print(parent.cmd.randomize_ports, *arg);
16743    }
16744    for (int i=1; i < algo_lib::_db.cmdline.verbose; ++i) {
16745        ary_Alloc(args) << "-verbose";
16746    }
16747}
16748
16749// --- command.atf_ws_proc..Uninit
16750void command::atf_ws_proc_Uninit(command::atf_ws_proc& parent) {
16751    command::atf_ws_proc &row = parent; (void)row;
16752
16753    // command.atf_ws_proc.atf_ws.Uninit (Exec)  //
16754    atf_ws_Kill(parent); // kill child, ensure forward progress
16755}
16756
16757// --- command.bash..PrintArgv
16758// print string representation of ROW to string STR
16759// cfmt:command.bash.ArgvGnu  printfmt:Auto
16760void command::bash_PrintArgv(command::bash& row, algo::cstring& str) {
16761    algo::tempstr temp;
16762    (void)temp;
16763    (void)str;
16764    if (!(row.c == "")) {
16765        ch_RemoveAll(temp);
16766        cstring_Print(row.c, temp);
16767        str << " -c ";
16768        strptr_PrintBash(temp,str);
16769    }
16770}
16771
16772// --- command.bash2html..ReadFieldMaybe
16773bool command::bash2html_ReadFieldMaybe(command::bash2html& parent, algo::strptr field, algo::strptr strval) {
16774    bool retval = true;
16775    command::FieldId field_id;
16776    (void)value_SetStrptrMaybe(field_id,field);
16777    switch(field_id) {
16778        case command_FieldId_in: {
16779            retval = algo::cstring_ReadStrptrMaybe(parent.in, strval);
16780            break;
16781        }
16782        case command_FieldId_test: {
16783            retval = bool_ReadStrptrMaybe(parent.test, strval);
16784            break;
16785        }
16786        default: break;
16787    }
16788    if (!retval) {
16789        algo_lib::AppendErrtext("attr",field);
16790    }
16791    return retval;
16792}
16793
16794// --- command.bash2html..ReadTupleMaybe
16795// Read fields of command::bash2html from attributes of ascii tuple TUPLE
16796bool command::bash2html_ReadTupleMaybe(command::bash2html &parent, algo::Tuple &tuple) {
16797    bool retval = true;
16798    ind_beg(algo::Tuple_attrs_curs,attr,tuple) {
16799        retval = bash2html_ReadFieldMaybe(parent, attr.name, attr.value);
16800        if (!retval) {
16801            break;
16802        }
16803    }ind_end;
16804    return retval;
16805}
16806
16807// --- command.bash2html..ToCmdline
16808// Convenience function that returns a full command line
16809// Assume command is in a directory called bin
16810tempstr command::bash2html_ToCmdline(command::bash2html& row) {
16811    tempstr ret;
16812    ret << "bin/bash2html ";
16813    bash2html_PrintArgv(row, ret);
16814    // inherit less intense verbose, debug options
16815    for (int i = 1; i < algo_lib::_db.cmdline.verbose; i++) {
16816        ret << " -verbose";
16817    }
16818    for (int i = 1; i < algo_lib::_db.cmdline.debug; i++) {
16819        ret << " -debug";
16820    }
16821    return ret;
16822}
16823
16824// --- command.bash2html..PrintArgv
16825// print string representation of ROW to string STR
16826// cfmt:command.bash2html.Argv  printfmt:Tuple
16827void command::bash2html_PrintArgv(command::bash2html& row, algo::cstring& str) {
16828    algo::tempstr temp;
16829    (void)temp;
16830    (void)str;
16831    if (!(row.in == "data")) {
16832        ch_RemoveAll(temp);
16833        cstring_Print(row.in, temp);
16834        str << " -in:";
16835        strptr_PrintBash(temp,str);
16836    }
16837    if (!(row.test == false)) {
16838        ch_RemoveAll(temp);
16839        bool_Print(row.test, temp);
16840        str << " -test:";
16841        strptr_PrintBash(temp,str);
16842    }
16843}
16844
16845// --- command.bash2html..NArgs
16846// Used with command lines
16847// Return # of command-line arguments that must follow this argument
16848// If FIELD is invalid, return -1
16849i32 command::bash2html_NArgs(command::FieldId field, algo::strptr& out_dflt, bool* out_anon) {
16850    i32 retval = 1;
16851    switch (field) {
16852        case command_FieldId_in: { // $comment
16853            *out_anon = false;
16854        } break;
16855        case command_FieldId_test: { // $comment
16856            *out_anon = false;
16857            retval=0;
16858            out_dflt="Y";
16859        } break;
16860        default:
16861        retval=-1; // unrecognized
16862    }
16863    return retval;
16864}
16865
16866// --- command.bash2html_proc.bash2html.Start
16867// Start subprocess
16868// If subprocess already running, do nothing. Otherwise, start it
16869int command::bash2html_Start(command::bash2html_proc& parent) {
16870    int retval = 0;
16871    if (parent.pid == 0) {
16872        verblog(bash2html_ToCmdline(parent)); // maybe print command
16873#ifdef WIN32
16874        algo_lib::ResolveExecFname(parent.path);
16875        tempstr cmdline(bash2html_ToCmdline(parent));
16876        parent.pid = dospawn(Zeroterm(parent.path),Zeroterm(cmdline),parent.timeout,parent.fstdin,parent.fstdout,parent.fstderr);
16877#else
16878        parent.pid = fork();
16879        if (parent.pid == 0) { // child
16880            algo_lib::DieWithParent();
16881            if (parent.timeout > 0) {
16882                alarm(parent.timeout);
16883            }
16884            if (retval==0) retval=algo_lib::ApplyRedirect(parent.fstdin , 0);
16885            if (retval==0) retval=algo_lib::ApplyRedirect(parent.fstdout, 1);
16886            if (retval==0) retval=algo_lib::ApplyRedirect(parent.fstderr, 2);
16887            if (retval==0) retval= bash2html_Execv(parent);
16888            if (retval != 0) { // if start fails, print error
16889                int err=errno;
16890                prerr("command.bash2html_execv"
16891                <<Keyval("errno",err)
16892                <<Keyval("errstr",strerror(err))
16893                <<Keyval("comment","Execv failed"));
16894            }
16895            _exit(127); // if failed to start, exit anyway
16896        } else if (parent.pid == -1) {
16897            retval = errno; // failed to fork
16898        }
16899#endif
16900    }
16901    parent.status = parent.pid > 0 ? 0 : -1; // if didn't start, set error status
16902    return retval;
16903}
16904
16905// --- command.bash2html_proc.bash2html.StartRead
16906// Start subprocess & Read output
16907algo::Fildes command::bash2html_StartRead(command::bash2html_proc& parent, algo_lib::FFildes &read) {
16908    int pipefd[2];
16909    int rc=pipe(pipefd);
16910    (void)rc;
16911    read.fd.value = pipefd[0];
16912    parent.fstdout  << ">&" << pipefd[1];
16913    bash2html_Start(parent);
16914    (void)close(pipefd[1]);
16915    return read.fd;
16916}
16917
16918// --- command.bash2html_proc.bash2html.Kill
16919// Kill subprocess and wait
16920void command::bash2html_Kill(command::bash2html_proc& parent) {
16921    if (parent.pid != 0) {
16922        kill(parent.pid,9);
16923        bash2html_Wait(parent);
16924    }
16925}
16926
16927// --- command.bash2html_proc.bash2html.Wait
16928// Wait for subprocess to return
16929void command::bash2html_Wait(command::bash2html_proc& parent) {
16930    if (parent.pid > 0) {
16931        int wait_flags = 0;
16932        int wait_status = 0;
16933        int rc = -1;
16934        do {
16935            // really wait for subprocess to exit
16936            rc = waitpid(parent.pid,&wait_status,wait_flags);
16937        } while (rc==-1 && errno==EINTR);
16938        if (rc == parent.pid) {
16939            parent.status = wait_status;
16940            parent.pid = 0;
16941        }
16942    }
16943}
16944
16945// --- command.bash2html_proc.bash2html.Exec
16946// Start + Wait
16947// Execute subprocess and return exit code
16948int command::bash2html_Exec(command::bash2html_proc& parent) {
16949    bash2html_Start(parent);
16950    bash2html_Wait(parent);
16951    return parent.status;
16952}
16953
16954// --- command.bash2html_proc.bash2html.ExecX
16955// Start + Wait, throw exception on error
16956// Execute subprocess; throw human-readable exception on error
16957void command::bash2html_ExecX(command::bash2html_proc& parent) {
16958    int rc = bash2html_Exec(parent);
16959    vrfy(rc==0, tempstr() << "algo_lib.exec" << Keyval("cmd",bash2html_ToCmdline(parent))
16960    << Keyval("comment",algo::DescribeWaitStatus(parent.status)));
16961}
16962
16963// --- command.bash2html_proc.bash2html.Execv
16964// Call execv()
16965// Call execv with specified parameters
16966int command::bash2html_Execv(command::bash2html_proc& parent) {
16967    int ret = 0;
16968    algo::StringAry args;
16969    bash2html_ToArgv(parent, args);
16970    char **argv = (char**)alloca((ary_N(args)+1)*sizeof(*argv));
16971    ind_beg(algo::StringAry_ary_curs,arg,args) {
16972        argv[ind_curs(arg).index] = Zeroterm(arg);
16973    }ind_end;
16974    argv[ary_N(args)] = NULL;
16975    // if parent.path is relative, search for it in PATH
16976    algo_lib::ResolveExecFname(parent.path);
16977    ret = execv(Zeroterm(parent.path),argv);
16978    return ret;
16979}
16980
16981// --- command.bash2html_proc.bash2html.ToCmdline
16982algo::tempstr command::bash2html_ToCmdline(command::bash2html_proc& parent) {
16983    algo::tempstr retval;
16984    retval << parent.path << " ";
16985    command::bash2html_PrintArgv(parent.cmd,retval);
16986    if (ch_N(parent.fstdin)) {
16987        retval << " " << parent.fstdin;
16988    }
16989    if (ch_N(parent.fstdout)) {
16990        retval << " " << parent.fstdout;
16991    }
16992    if (ch_N(parent.fstderr)) {
16993        retval << " 2" << parent.fstderr;
16994    }
16995    return retval;
16996}
16997
16998// --- command.bash2html_proc.bash2html.ToArgv
16999// Form array from the command line
17000void command::bash2html_ToArgv(command::bash2html_proc& parent, algo::StringAry& args) {
17001    ary_RemoveAll(args);
17002    ary_Alloc(args) << parent.path;
17003
17004    if (parent.cmd.in != "data") {
17005        cstring *arg = &ary_Alloc(args);
17006        *arg << "-in:";
17007        cstring_Print(parent.cmd.in, *arg);
17008    }
17009
17010    if (parent.cmd.test != false) {
17011        cstring *arg = &ary_Alloc(args);
17012        *arg << "-test:";
17013        bool_Print(parent.cmd.test, *arg);
17014    }
17015    for (int i=1; i < algo_lib::_db.cmdline.verbose; ++i) {
17016        ary_Alloc(args) << "-verbose";
17017    }
17018}
17019
17020// --- command.bash2html_proc..Uninit
17021void command::bash2html_proc_Uninit(command::bash2html_proc& parent) {
17022    command::bash2html_proc &row = parent; (void)row;
17023
17024    // command.bash2html_proc.bash2html.Uninit (Exec)  //
17025    bash2html_Kill(parent); // kill child, ensure forward progress
17026}
17027
17028// --- command.bash_proc.bash.Start
17029// Start subprocess
17030// If subprocess already running, do nothing. Otherwise, start it
17031int command::bash_Start(command::bash_proc& parent) {
17032    int retval = 0;
17033    if (parent.pid == 0) {
17034        verblog(bash_ToCmdline(parent)); // maybe print command
17035#ifdef WIN32
17036        algo_lib::ResolveExecFname(parent.path);
17037        tempstr cmdline(bash_ToCmdline(parent));
17038        parent.pid = dospawn(Zeroterm(parent.path),Zeroterm(cmdline),parent.timeout,parent.fstdin,parent.fstdout,parent.fstderr);
17039#else
17040        parent.pid = fork();
17041        if (parent.pid == 0) { // child
17042            algo_lib::DieWithParent();
17043            if (parent.timeout > 0) {
17044                alarm(parent.timeout);
17045            }
17046            if (retval==0) retval=algo_lib::ApplyRedirect(parent.fstdin , 0);
17047            if (retval==0) retval=algo_lib::ApplyRedirect(parent.fstdout, 1);
17048            if (retval==0) retval=algo_lib::ApplyRedirect(parent.fstderr, 2);
17049            if (retval==0) retval= bash_Execv(parent);
17050            if (retval != 0) { // if start fails, print error
17051                int err=errno;
17052                prerr("command.bash_execv"
17053                <<Keyval("errno",err)
17054                <<Keyval("errstr",strerror(err))
17055                <<Keyval("comment","Execv failed"));
17056            }
17057            _exit(127); // if failed to start, exit anyway
17058        } else if (parent.pid == -1) {
17059            retval = errno; // failed to fork
17060        }
17061#endif
17062    }
17063    parent.status = parent.pid > 0 ? 0 : -1; // if didn't start, set error status
17064    return retval;
17065}
17066
17067// --- command.bash_proc.bash.StartRead
17068// Start subprocess & Read output
17069algo::Fildes command::bash_StartRead(command::bash_proc& parent, algo_lib::FFildes &read) {
17070    int pipefd[2];
17071    int rc=pipe(pipefd);
17072    (void)rc;
17073    read.fd.value = pipefd[0];
17074    parent.fstdout  << ">&" << pipefd[1];
17075    bash_Start(parent);
17076    (void)close(pipefd[1]);
17077    return read.fd;
17078}
17079
17080// --- command.bash_proc.bash.Kill
17081// Kill subprocess and wait
17082void command::bash_Kill(command::bash_proc& parent) {
17083    if (parent.pid != 0) {
17084        kill(parent.pid,9);
17085        bash_Wait(parent);
17086    }
17087}
17088
17089// --- command.bash_proc.bash.Wait
17090// Wait for subprocess to return
17091void command::bash_Wait(command::bash_proc& parent) {
17092    if (parent.pid > 0) {
17093        int wait_flags = 0;
17094        int wait_status = 0;
17095        int rc = -1;
17096        do {
17097            // really wait for subprocess to exit
17098            rc = waitpid(parent.pid,&wait_status,wait_flags);
17099        } while (rc==-1 && errno==EINTR);
17100        if (rc == parent.pid) {
17101            parent.status = wait_status;
17102            parent.pid = 0;
17103        }
17104    }
17105}
17106
17107// --- command.bash_proc.bash.Exec
17108// Start + Wait
17109// Execute subprocess and return exit code
17110int command::bash_Exec(command::bash_proc& parent) {
17111    bash_Start(parent);
17112    bash_Wait(parent);
17113    return parent.status;
17114}
17115
17116// --- command.bash_proc.bash.ExecX
17117// Start + Wait, throw exception on error
17118// Execute subprocess; throw human-readable exception on error
17119void command::bash_ExecX(command::bash_proc& parent) {
17120    int rc = bash_Exec(parent);
17121    vrfy(rc==0, tempstr() << "algo_lib.exec" << Keyval("cmd",bash_ToCmdline(parent))
17122    << Keyval("comment",algo::DescribeWaitStatus(parent.status)));
17123}
17124
17125// --- command.bash_proc.bash.Execv
17126// Call execv()
17127// Call execv with specified parameters
17128int command::bash_Execv(command::bash_proc& parent) {
17129    int ret = 0;
17130    algo::StringAry args;
17131    bash_ToArgv(parent, args);
17132    char **argv = (char**)alloca((ary_N(args)+1)*sizeof(*argv));
17133    ind_beg(algo::StringAry_ary_curs,arg,args) {
17134        argv[ind_curs(arg).index] = Zeroterm(arg);
17135    }ind_end;
17136    argv[ary_N(args)] = NULL;
17137    // if parent.path is relative, search for it in PATH
17138    algo_lib::ResolveExecFname(parent.path);
17139    ret = execv(Zeroterm(parent.path),argv);
17140    return ret;
17141}
17142
17143// --- command.bash_proc.bash.ToCmdline
17144algo::tempstr command::bash_ToCmdline(command::bash_proc& parent) {
17145    algo::tempstr retval;
17146    retval << parent.path << " ";
17147    command::bash_PrintArgv(parent.cmd,retval);
17148    if (ch_N(parent.fstdin)) {
17149        retval << " " << parent.fstdin;
17150    }
17151    if (ch_N(parent.fstdout)) {
17152        retval << " " << parent.fstdout;
17153    }
17154    if (ch_N(parent.fstderr)) {
17155        retval << " 2" << parent.fstderr;
17156    }
17157    return retval;
17158}
17159
17160// --- command.bash_proc.bash.ToArgv
17161// Form array from the command line
17162void command::bash_ToArgv(command::bash_proc& parent, algo::StringAry& args) {
17163    ary_RemoveAll(args);
17164    ary_Alloc(args) << parent.path;
17165
17166    if (parent.cmd.c != "") {
17167        ary_Alloc(args) << "-c";
17168        cstring *arg = &ary_Alloc(args);
17169        cstring_Print(parent.cmd.c, *arg);
17170    }
17171}
17172
17173// --- command.bash_proc..Uninit
17174void command::bash_proc_Uninit(command::bash_proc& parent) {
17175    command::bash_proc &row = parent; (void)row;
17176
17177    // command.bash_proc.bash.Uninit (Exec)  //Must be bash to support $'' for string quoting
17178    bash_Kill(parent); // kill child, ensure forward progress
17179}
17180
17181// --- command.dcli.dhost.Print
17182// Print back to string
17183void command::dhost_Print(command::dcli& parent, algo::cstring &out) {
17184    Regx_Print(parent.dhost, out);
17185}
17186
17187// --- command.dcli.dhost.ReadStrptrMaybe
17188// Read Regx from string
17189// Convert string to field. Return success value
17190bool command::dhost_ReadStrptrMaybe(command::dcli& parent, algo::strptr in) {
17191    bool retval = true;
17192    Regx_ReadSql(parent.dhost, in, true);
17193    return retval;
17194}
17195
17196// --- command.dcli.dctr.Print
17197// Print back to string
17198void command::dctr_Print(command::dcli& parent, algo::cstring &out) {
17199    Regx_Print(parent.dctr, out);
17200}
17201
17202// --- command.dcli.dctr.ReadStrptrMaybe
17203// Read Regx from string
17204// Convert string to field. Return success value
17205bool command::dctr_ReadStrptrMaybe(command::dcli& parent, algo::strptr in) {
17206    bool retval = true;
17207    Regx_ReadSql(parent.dctr, in, true);
17208    return retval;
17209}
17210
17211// --- command.dcli.dctrhost.Print
17212// Print back to string
17213void command::dctrhost_Print(command::dcli& parent, algo::cstring &out) {
17214    Regx_Print(parent.dctrhost, out);
17215}
17216
17217// --- command.dcli.dctrhost.ReadStrptrMaybe
17218// Read Regx from string
17219// Convert string to field. Return success value
17220bool command::dctrhost_ReadStrptrMaybe(command::dcli& parent, algo::strptr in) {
17221    bool retval = true;
17222    Regx_ReadSql(parent.dctrhost, in, true);
17223    return retval;
17224}
17225
17226// --- command.dcli..ReadFieldMaybe
17227bool command::dcli_ReadFieldMaybe(command::dcli& parent, algo::strptr field, algo::strptr strval) {
17228    bool retval = true;
17229    command::FieldId field_id;
17230    (void)value_SetStrptrMaybe(field_id,field);
17231    switch(field_id) {
17232        case command_FieldId_in: {
17233            retval = algo::cstring_ReadStrptrMaybe(parent.in, strval);
17234            break;
17235        }
17236        case command_FieldId_cmd: {
17237            retval = algo::cstring_ReadStrptrMaybe(parent.cmd, strval);
17238            break;
17239        }
17240        case command_FieldId_cmd_dctr: {
17241            retval = algo::cstring_ReadStrptrMaybe(parent.cmd_dctr, strval);
17242            break;
17243        }
17244        case command_FieldId_config_ssh: {
17245            retval = bool_ReadStrptrMaybe(parent.config_ssh, strval);
17246            break;
17247        }
17248        case command_FieldId_config_ssh_vpn: {
17249            retval = bool_ReadStrptrMaybe(parent.config_ssh_vpn, strval);
17250            break;
17251        }
17252        case command_FieldId_clean_run: {
17253            retval = bool_ReadStrptrMaybe(parent.clean_run, strval);
17254            break;
17255        }
17256        case command_FieldId_shell: {
17257            retval = bool_ReadStrptrMaybe(parent.shell, strval);
17258            break;
17259        }
17260        case command_FieldId_inspect: {
17261            retval = bool_ReadStrptrMaybe(parent.inspect, strval);
17262            break;
17263        }
17264        case command_FieldId_logs: {
17265            retval = bool_ReadStrptrMaybe(parent.logs, strval);
17266            break;
17267        }
17268        case command_FieldId_generate_build: {
17269            retval = bool_ReadStrptrMaybe(parent.generate_build, strval);
17270            break;
17271        }
17272        case command_FieldId_build: {
17273            retval = bool_ReadStrptrMaybe(parent.build, strval);
17274            break;
17275        }
17276        case command_FieldId_dhost: {
17277            retval = dhost_ReadStrptrMaybe(parent, strval);
17278            break;
17279        }
17280        case command_FieldId_dctr: {
17281            retval = dctr_ReadStrptrMaybe(parent, strval);
17282            break;
17283        }
17284        case command_FieldId_dctrhost: {
17285            retval = dctrhost_ReadStrptrMaybe(parent, strval);
17286            break;
17287        }
17288        case command_FieldId_t: {
17289            retval = bool_ReadStrptrMaybe(parent.t, strval);
17290            break;
17291        }
17292        case command_FieldId_q: {
17293            retval = bool_ReadStrptrMaybe(parent.q, strval);
17294            break;
17295        }
17296        case command_FieldId_dry_run: {
17297            retval = bool_ReadStrptrMaybe(parent.dry_run, strval);
17298            break;
17299        }
17300        default: break;
17301    }
17302    if (!retval) {
17303        algo_lib::AppendErrtext("attr",field);
17304    }
17305    return retval;
17306}
17307
17308// --- command.dcli..ReadTupleMaybe
17309// Read fields of command::dcli from attributes of ascii tuple TUPLE
17310bool command::dcli_ReadTupleMaybe(command::dcli &parent, algo::Tuple &tuple) {
17311    bool retval = true;
17312    int anon_idx = 0;
17313    ind_beg(algo::Tuple_attrs_curs,attr,tuple) {
17314        if (ch_N(attr.name) == 0) {
17315            attr.name = dcli_GetAnon(parent, anon_idx++);
17316        }
17317        retval = dcli_ReadFieldMaybe(parent, attr.name, attr.value);
17318        if (!retval) {
17319            break;
17320        }
17321    }ind_end;
17322    return retval;
17323}
17324
17325// --- command.dcli..Init
17326// Set all fields to initial values.
17327void command::dcli_Init(command::dcli& parent) {
17328    parent.in = algo::strptr("data");
17329    parent.cmd = algo::strptr("");
17330    parent.cmd_dctr = algo::strptr("");
17331    parent.config_ssh = bool(false);
17332    parent.config_ssh_vpn = bool(false);
17333    parent.clean_run = bool(false);
17334    parent.shell = bool(false);
17335    parent.inspect = bool(false);
17336    parent.logs = bool(false);
17337    parent.generate_build = bool(false);
17338    parent.build = bool(false);
17339    Regx_ReadSql(parent.dhost, "", true);
17340    Regx_ReadSql(parent.dctr, "", true);
17341    Regx_ReadSql(parent.dctrhost, "", true);
17342    parent.t = bool(false);
17343    parent.q = bool(false);
17344    parent.dry_run = bool(false);
17345}
17346
17347// --- command.dcli..ToCmdline
17348// Convenience function that returns a full command line
17349// Assume command is in a directory called bin
17350tempstr command::dcli_ToCmdline(command::dcli& row) {
17351    tempstr ret;
17352    ret << "bin/dcli ";
17353    dcli_PrintArgv(row, ret);
17354    // inherit less intense verbose, debug options
17355    for (int i = 1; i < algo_lib::_db.cmdline.verbose; i++) {
17356        ret << " -verbose";
17357    }
17358    for (int i = 1; i < algo_lib::_db.cmdline.debug; i++) {
17359        ret << " -debug";
17360    }
17361    return ret;
17362}
17363
17364// --- command.dcli..PrintArgv
17365// print string representation of ROW to string STR
17366// cfmt:command.dcli.Argv  printfmt:Tuple
17367void command::dcli_PrintArgv(command::dcli& row, algo::cstring& str) {
17368    algo::tempstr temp;
17369    (void)temp;
17370    (void)str;
17371    if (!(row.in == "data")) {
17372        ch_RemoveAll(temp);
17373        cstring_Print(row.in, temp);
17374        str << " -in:";
17375        strptr_PrintBash(temp,str);
17376    }
17377    ch_RemoveAll(temp);
17378    cstring_Print(row.cmd, temp);
17379    str << " -cmd:";
17380    strptr_PrintBash(temp,str);
17381    if (!(row.cmd_dctr == "")) {
17382        ch_RemoveAll(temp);
17383        cstring_Print(row.cmd_dctr, temp);
17384        str << " -cmd_dctr:";
17385        strptr_PrintBash(temp,str);
17386    }
17387    if (!(row.config_ssh == false)) {
17388        ch_RemoveAll(temp);
17389        bool_Print(row.config_ssh, temp);
17390        str << " -config_ssh:";
17391        strptr_PrintBash(temp,str);
17392    }
17393    if (!(row.config_ssh_vpn == false)) {
17394        ch_RemoveAll(temp);
17395        bool_Print(row.config_ssh_vpn, temp);
17396        str << " -config_ssh_vpn:";
17397        strptr_PrintBash(temp,str);
17398    }
17399    if (!(row.clean_run == false)) {
17400        ch_RemoveAll(temp);
17401        bool_Print(row.clean_run, temp);
17402        str << " -clean_run:";
17403        strptr_PrintBash(temp,str);
17404    }
17405    if (!(row.shell == false)) {
17406        ch_RemoveAll(temp);
17407        bool_Print(row.shell, temp);
17408        str << " -shell:";
17409        strptr_PrintBash(temp,str);
17410    }
17411    if (!(row.inspect == false)) {
17412        ch_RemoveAll(temp);
17413        bool_Print(row.inspect, temp);
17414        str << " -inspect:";
17415        strptr_PrintBash(temp,str);
17416    }
17417    if (!(row.logs == false)) {
17418        ch_RemoveAll(temp);
17419        bool_Print(row.logs, temp);
17420        str << " -logs:";
17421        strptr_PrintBash(temp,str);
17422    }
17423    if (!(row.generate_build == false)) {
17424        ch_RemoveAll(temp);
17425        bool_Print(row.generate_build, temp);
17426        str << " -generate_build:";
17427        strptr_PrintBash(temp,str);
17428    }
17429    if (!(row.build == false)) {
17430        ch_RemoveAll(temp);
17431        bool_Print(row.build, temp);
17432        str << " -build:";
17433        strptr_PrintBash(temp,str);
17434    }
17435    if (!(row.dhost.expr == "")) {
17436        ch_RemoveAll(temp);
17437        command::dhost_Print(const_cast<command::dcli&>(row), temp);
17438        str << " -dhost:";
17439        strptr_PrintBash(temp,str);
17440    }
17441    if (!(row.dctr.expr == "")) {
17442        ch_RemoveAll(temp);
17443        command::dctr_Print(const_cast<command::dcli&>(row), temp);
17444        str << " -dctr:";
17445        strptr_PrintBash(temp,str);
17446    }
17447    if (!(row.dctrhost.expr == "")) {
17448        ch_RemoveAll(temp);
17449        command::dctrhost_Print(const_cast<command::dcli&>(row), temp);
17450        str << " -dctrhost:";
17451        strptr_PrintBash(temp,str);
17452    }
17453    if (!(row.t == false)) {
17454        ch_RemoveAll(temp);
17455        bool_Print(row.t, temp);
17456        str << " -t:";
17457        strptr_PrintBash(temp,str);
17458    }
17459    if (!(row.q == false)) {
17460        ch_RemoveAll(temp);
17461        bool_Print(row.q, temp);
17462        str << " -q:";
17463        strptr_PrintBash(temp,str);
17464    }
17465    if (!(row.dry_run == false)) {
17466        ch_RemoveAll(temp);
17467        bool_Print(row.dry_run, temp);
17468        str << " -dry_run:";
17469        strptr_PrintBash(temp,str);
17470    }
17471}
17472
17473// --- command.dcli..GetAnon
17474algo::strptr command::dcli_GetAnon(command::dcli &parent, i32 idx) {
17475    (void)parent;//only to avoid -Wunused-parameter
17476    switch(idx) {
17477        case(0): return strptr("cmd", 3);
17478        default: return algo::strptr();
17479    }
17480}
17481
17482// --- command.dcli..NArgs
17483// Used with command lines
17484// Return # of command-line arguments that must follow this argument
17485// If FIELD is invalid, return -1
17486i32 command::dcli_NArgs(command::FieldId field, algo::strptr& out_dflt, bool* out_anon) {
17487    i32 retval = 1;
17488    switch (field) {
17489        case command_FieldId_in: { // $comment
17490            *out_anon = false;
17491        } break;
17492        case command_FieldId_cmd: { // $comment
17493            *out_anon = true;
17494        } break;
17495        case command_FieldId_cmd_dctr: { // $comment
17496            *out_anon = false;
17497        } break;
17498        case command_FieldId_config_ssh: { // $comment
17499            *out_anon = false;
17500            retval=0;
17501            out_dflt="Y";
17502        } break;
17503        case command_FieldId_config_ssh_vpn: { // bool: no argument required but value may be specified as config_ssh:Y
17504            *out_anon = false;
17505            retval=0;
17506            out_dflt="Y";
17507        } break;
17508        case command_FieldId_clean_run: { // bool: no argument required but value may be specified as config_ssh_vpn:Y
17509            *out_anon = false;
17510            retval=0;
17511            out_dflt="Y";
17512        } break;
17513        case command_FieldId_shell: { // bool: no argument required but value may be specified as clean_run:Y
17514            *out_anon = false;
17515            retval=0;
17516            out_dflt="Y";
17517        } break;
17518        case command_FieldId_inspect: { // bool: no argument required but value may be specified as shell:Y
17519            *out_anon = false;
17520            retval=0;
17521            out_dflt="Y";
17522        } break;
17523        case command_FieldId_logs: { // bool: no argument required but value may be specified as inspect:Y
17524            *out_anon = false;
17525            retval=0;
17526            out_dflt="Y";
17527        } break;
17528        case command_FieldId_generate_build: { // bool: no argument required but value may be specified as logs:Y
17529            *out_anon = false;
17530            retval=0;
17531            out_dflt="Y";
17532        } break;
17533        case command_FieldId_build: { // bool: no argument required but value may be specified as generate_build:Y
17534            *out_anon = false;
17535            retval=0;
17536            out_dflt="Y";
17537        } break;
17538        case command_FieldId_dhost: { // bool: no argument required but value may be specified as build:Y
17539            *out_anon = false;
17540        } break;
17541        case command_FieldId_dctr: { // bool: no argument required but value may be specified as build:Y
17542            *out_anon = false;
17543        } break;
17544        case command_FieldId_dctrhost: { // bool: no argument required but value may be specified as build:Y
17545            *out_anon = false;
17546        } break;
17547        case command_FieldId_t: { // bool: no argument required but value may be specified as build:Y
17548            *out_anon = false;
17549            retval=0;
17550            out_dflt="Y";
17551        } break;
17552        case command_FieldId_q: { // bool: no argument required but value may be specified as t:Y
17553            *out_anon = false;
17554            retval=0;
17555            out_dflt="Y";
17556        } break;
17557        case command_FieldId_dry_run: { // bool: no argument required but value may be specified as q:Y
17558            *out_anon = false;
17559            retval=0;
17560            out_dflt="Y";
17561        } break;
17562        default:
17563        retval=-1; // unrecognized
17564    }
17565    return retval;
17566}
17567
17568// --- command.dcli_ed..ReadFieldMaybe
17569bool command::dcli_ed_ReadFieldMaybe(command::dcli_ed& parent, algo::strptr field, algo::strptr strval) {
17570    bool retval = true;
17571    command::FieldId field_id;
17572    (void)value_SetStrptrMaybe(field_id,field);
17573    switch(field_id) {
17574        case command_FieldId_in: {
17575            retval = algo::cstring_ReadStrptrMaybe(parent.in, strval);
17576            break;
17577        }
17578        case command_FieldId_script_dir: {
17579            retval = algo::cstring_ReadStrptrMaybe(parent.script_dir, strval);
17580            break;
17581        }
17582        case command_FieldId_target: {
17583            retval = algo::Smallstr50_ReadStrptrMaybe(parent.target, strval);
17584            break;
17585        }
17586        case command_FieldId_dctr: {
17587            retval = algo::Smallstr50_ReadStrptrMaybe(parent.dctr, strval);
17588            break;
17589        }
17590        case command_FieldId_dhost: {
17591            retval = algo::Smallstr50_ReadStrptrMaybe(parent.dhost, strval);
17592            break;
17593        }
17594        case command_FieldId_dctrhost: {
17595            retval = algo::Smallstr50_ReadStrptrMaybe(parent.dctrhost, strval);
17596            break;
17597        }
17598        case command_FieldId_create: {
17599            retval = bool_ReadStrptrMaybe(parent.create, strval);
17600            break;
17601        }
17602        case command_FieldId_cloneto: {
17603            retval = algo::Smallstr50_ReadStrptrMaybe(parent.cloneto, strval);
17604            break;
17605        }
17606        case command_FieldId_renameto: {
17607            retval = algo::Smallstr50_ReadStrptrMaybe(parent.renameto, strval);
17608            break;
17609        }
17610        case command_FieldId_del: {
17611            retval = bool_ReadStrptrMaybe(parent.del, strval);
17612            break;
17613        }
17614        case command_FieldId_purge: {
17615            retval = bool_ReadStrptrMaybe(parent.purge, strval);
17616            break;
17617        }
17618        case command_FieldId_write: {
17619            retval = bool_ReadStrptrMaybe(parent.write, strval);
17620            break;
17621        }
17622        case command_FieldId_force: {
17623            retval = bool_ReadStrptrMaybe(parent.force, strval);
17624            break;
17625        }
17626        case command_FieldId_deep: {
17627            retval = bool_ReadStrptrMaybe(parent.deep, strval);
17628            break;
17629        }
17630        case command_FieldId_sync: {
17631            retval = bool_ReadStrptrMaybe(parent.sync, strval);
17632            break;
17633        }
17634        case command_FieldId_keep_files: {
17635            retval = bool_ReadStrptrMaybe(parent.keep_files, strval);
17636            break;
17637        }
17638        case command_FieldId_comment: {
17639            retval = algo::Comment_ReadStrptrMaybe(parent.comment, strval);
17640            break;
17641        }
17642        case command_FieldId_dctr_from: {
17643            retval = algo::cstring_ReadStrptrMaybe(parent.dctr_from, strval);
17644            break;
17645        }
17646        case command_FieldId_dhost_ip: {
17647            retval = algo::cstring_ReadStrptrMaybe(parent.dhost_ip, strval);
17648            break;
17649        }
17650        case command_FieldId_dhost_ext_ip: {
17651            retval = algo::cstring_ReadStrptrMaybe(parent.dhost_ext_ip, strval);
17652            break;
17653        }
17654        case command_FieldId_dhost_ext_port: {
17655            retval = algo::cstring_ReadStrptrMaybe(parent.dhost_ext_port, strval);
17656            break;
17657        }
17658        case command_FieldId_dhost_user: {
17659            retval = algo::cstring_ReadStrptrMaybe(parent.dhost_user, strval);
17660            break;
17661        }
17662        default: break;
17663    }
17664    if (!retval) {
17665        algo_lib::AppendErrtext("attr",field);
17666    }
17667    return retval;
17668}
17669
17670// --- command.dcli_ed..ReadTupleMaybe
17671// Read fields of command::dcli_ed from attributes of ascii tuple TUPLE
17672bool command::dcli_ed_ReadTupleMaybe(command::dcli_ed &parent, algo::Tuple &tuple) {
17673    bool retval = true;
17674    int anon_idx = 0;
17675    ind_beg(algo::Tuple_attrs_curs,attr,tuple) {
17676        if (ch_N(attr.name) == 0) {
17677            attr.name = dcli_ed_GetAnon(parent, anon_idx++);
17678        }
17679        retval = dcli_ed_ReadFieldMaybe(parent, attr.name, attr.value);
17680        if (!retval) {
17681            break;
17682        }
17683    }ind_end;
17684    return retval;
17685}
17686
17687// --- command.dcli_ed..Init
17688// Set all fields to initial values.
17689void command::dcli_ed_Init(command::dcli_ed& parent) {
17690    parent.in = algo::strptr("data");
17691    parent.script_dir = algo::strptr("docker");
17692    parent.target = algo::strptr("");
17693    parent.dctr = algo::strptr("");
17694    parent.dhost = algo::strptr("");
17695    parent.dctrhost = algo::strptr("");
17696    parent.create = bool(false);
17697    parent.cloneto = algo::strptr("");
17698    parent.renameto = algo::strptr("");
17699    parent.del = bool(false);
17700    parent.purge = bool(false);
17701    parent.write = bool(false);
17702    parent.force = bool(false);
17703    parent.deep = bool(false);
17704    parent.sync = bool(true);
17705    parent.keep_files = bool(false);
17706    parent.comment = algo::Comment("");
17707    parent.dctr_from = algo::strptr("ubuntu");
17708    parent.dhost_ip = algo::strptr("");
17709    parent.dhost_ext_ip = algo::strptr("");
17710    parent.dhost_ext_port = algo::strptr("3022");
17711    parent.dhost_user = algo::strptr("dcliusr");
17712}
17713
17714// --- command.dcli_ed..ToCmdline
17715// Convenience function that returns a full command line
17716// Assume command is in a directory called bin
17717tempstr command::dcli_ed_ToCmdline(command::dcli_ed& row) {
17718    tempstr ret;
17719    ret << "bin/dcli_ed ";
17720    dcli_ed_PrintArgv(row, ret);
17721    // inherit less intense verbose, debug options
17722    for (int i = 1; i < algo_lib::_db.cmdline.verbose; i++) {
17723        ret << " -verbose";
17724    }
17725    for (int i = 1; i < algo_lib::_db.cmdline.debug; i++) {
17726        ret << " -debug";
17727    }
17728    return ret;
17729}
17730
17731// --- command.dcli_ed..PrintArgv
17732// print string representation of ROW to string STR
17733// cfmt:command.dcli_ed.Argv  printfmt:Tuple
17734void command::dcli_ed_PrintArgv(command::dcli_ed& row, algo::cstring& str) {
17735    algo::tempstr temp;
17736    (void)temp;
17737    (void)str;
17738    if (!(row.in == "data")) {
17739        ch_RemoveAll(temp);
17740        cstring_Print(row.in, temp);
17741        str << " -in:";
17742        strptr_PrintBash(temp,str);
17743    }
17744    if (!(row.script_dir == "docker")) {
17745        ch_RemoveAll(temp);
17746        cstring_Print(row.script_dir, temp);
17747        str << " -script_dir:";
17748        strptr_PrintBash(temp,str);
17749    }
17750    ch_RemoveAll(temp);
17751    Smallstr50_Print(row.target, temp);
17752    str << " -target:";
17753    strptr_PrintBash(temp,str);
17754    if (!(row.dctr == "")) {
17755        ch_RemoveAll(temp);
17756        Smallstr50_Print(row.dctr, temp);
17757        str << " -dctr:";
17758        strptr_PrintBash(temp,str);
17759    }
17760    if (!(row.dhost == "")) {
17761        ch_RemoveAll(temp);
17762        Smallstr50_Print(row.dhost, temp);
17763        str << " -dhost:";
17764        strptr_PrintBash(temp,str);
17765    }
17766    if (!(row.dctrhost == "")) {
17767        ch_RemoveAll(temp);
17768        Smallstr50_Print(row.dctrhost, temp);
17769        str << " -dctrhost:";
17770        strptr_PrintBash(temp,str);
17771    }
17772    if (!(row.create == false)) {
17773        ch_RemoveAll(temp);
17774        bool_Print(row.create, temp);
17775        str << " -create:";
17776        strptr_PrintBash(temp,str);
17777    }
17778    if (!(row.cloneto == "")) {
17779        ch_RemoveAll(temp);
17780        Smallstr50_Print(row.cloneto, temp);
17781        str << " -cloneto:";
17782        strptr_PrintBash(temp,str);
17783    }
17784    if (!(row.renameto == "")) {
17785        ch_RemoveAll(temp);
17786        Smallstr50_Print(row.renameto, temp);
17787        str << " -renameto:";
17788        strptr_PrintBash(temp,str);
17789    }
17790    if (!(row.del == false)) {
17791        ch_RemoveAll(temp);
17792        bool_Print(row.del, temp);
17793        str << " -del:";
17794        strptr_PrintBash(temp,str);
17795    }
17796    if (!(row.purge == false)) {
17797        ch_RemoveAll(temp);
17798        bool_Print(row.purge, temp);
17799        str << " -purge:";
17800        strptr_PrintBash(temp,str);
17801    }
17802    if (!(row.write == false)) {
17803        ch_RemoveAll(temp);
17804        bool_Print(row.write, temp);
17805        str << " -write:";
17806        strptr_PrintBash(temp,str);
17807    }
17808    if (!(row.force == false)) {
17809        ch_RemoveAll(temp);
17810        bool_Print(row.force, temp);
17811        str << " -force:";
17812        strptr_PrintBash(temp,str);
17813    }
17814    if (!(row.deep == false)) {
17815        ch_RemoveAll(temp);
17816        bool_Print(row.deep, temp);
17817        str << " -deep:";
17818        strptr_PrintBash(temp,str);
17819    }
17820    if (!(row.sync == true)) {
17821        ch_RemoveAll(temp);
17822        bool_Print(row.sync, temp);
17823        str << " -sync:";
17824        strptr_PrintBash(temp,str);
17825    }
17826    if (!(row.keep_files == false)) {
17827        ch_RemoveAll(temp);
17828        bool_Print(row.keep_files, temp);
17829        str << " -keep_files:";
17830        strptr_PrintBash(temp,str);
17831    }
17832    if (!(row.comment == "")) {
17833        ch_RemoveAll(temp);
17834        Comment_Print(row.comment, temp);
17835        str << " -comment:";
17836        strptr_PrintBash(temp,str);
17837    }
17838    if (!(row.dctr_from == "ubuntu")) {
17839        ch_RemoveAll(temp);
17840        cstring_Print(row.dctr_from, temp);
17841        str << " -dctr_from:";
17842        strptr_PrintBash(temp,str);
17843    }
17844    if (!(row.dhost_ip == "")) {
17845        ch_RemoveAll(temp);
17846        cstring_Print(row.dhost_ip, temp);
17847        str << " -dhost_ip:";
17848        strptr_PrintBash(temp,str);
17849    }
17850    if (!(row.dhost_ext_ip == "")) {
17851        ch_RemoveAll(temp);
17852        cstring_Print(row.dhost_ext_ip, temp);
17853        str << " -dhost_ext_ip:";
17854        strptr_PrintBash(temp,str);
17855    }
17856    if (!(row.dhost_ext_port == "3022")) {
17857        ch_RemoveAll(temp);
17858        cstring_Print(row.dhost_ext_port, temp);
17859        str << " -dhost_ext_port:";
17860        strptr_PrintBash(temp,str);
17861    }
17862    if (!(row.dhost_user == "dcliusr")) {
17863        ch_RemoveAll(temp);
17864        cstring_Print(row.dhost_user, temp);
17865        str << " -dhost_user:";
17866        strptr_PrintBash(temp,str);
17867    }
17868}
17869
17870// --- command.dcli_ed..Print
17871// print string representation of ROW to string STR
17872// cfmt:command.dcli_ed.String  printfmt:Tuple
17873void command::dcli_ed_Print(command::dcli_ed& row, algo::cstring& str) {
17874    algo::tempstr temp;
17875    str << "command.dcli_ed";
17876
17877    algo::cstring_Print(row.in, temp);
17878    PrintAttrSpaceReset(str,"in", temp);
17879
17880    algo::cstring_Print(row.script_dir, temp);
17881    PrintAttrSpaceReset(str,"script_dir", temp);
17882
17883    algo::Smallstr50_Print(row.target, temp);
17884    PrintAttrSpaceReset(str,"target", temp);
17885
17886    algo::Smallstr50_Print(row.dctr, temp);
17887    PrintAttrSpaceReset(str,"dctr", temp);
17888
17889    algo::Smallstr50_Print(row.dhost, temp);
17890    PrintAttrSpaceReset(str,"dhost", temp);
17891
17892    algo::Smallstr50_Print(row.dctrhost, temp);
17893    PrintAttrSpaceReset(str,"dctrhost", temp);
17894
17895    bool_Print(row.create, temp);
17896    PrintAttrSpaceReset(str,"create", temp);
17897
17898    algo::Smallstr50_Print(row.cloneto, temp);
17899    PrintAttrSpaceReset(str,"cloneto", temp);
17900
17901    algo::Smallstr50_Print(row.renameto, temp);
17902    PrintAttrSpaceReset(str,"renameto", temp);
17903
17904    bool_Print(row.del, temp);
17905    PrintAttrSpaceReset(str,"del", temp);
17906
17907    bool_Print(row.purge, temp);
17908    PrintAttrSpaceReset(str,"purge", temp);
17909
17910    bool_Print(row.write, temp);
17911    PrintAttrSpaceReset(str,"write", temp);
17912
17913    bool_Print(row.force, temp);
17914    PrintAttrSpaceReset(str,"force", temp);
17915
17916    bool_Print(row.deep, temp);
17917    PrintAttrSpaceReset(str,"deep", temp);
17918
17919    bool_Print(row.sync, temp);
17920    PrintAttrSpaceReset(str,"sync", temp);
17921
17922    bool_Print(row.keep_files, temp);
17923    PrintAttrSpaceReset(str,"keep_files", temp);
17924
17925    algo::Comment_Print(row.comment, temp);
17926    PrintAttrSpaceReset(str,"comment", temp);
17927
17928    algo::cstring_Print(row.dctr_from, temp);
17929    PrintAttrSpaceReset(str,"dctr_from", temp);
17930
17931    algo::cstring_Print(row.dhost_ip, temp);
17932    PrintAttrSpaceReset(str,"dhost_ip", temp);
17933
17934    algo::cstring_Print(row.dhost_ext_ip, temp);
17935    PrintAttrSpaceReset(str,"dhost_ext_ip", temp);
17936
17937    algo::cstring_Print(row.dhost_ext_port, temp);
17938    PrintAttrSpaceReset(str,"dhost_ext_port", temp);
17939
17940    algo::cstring_Print(row.dhost_user, temp);
17941    PrintAttrSpaceReset(str,"dhost_user", temp);
17942}
17943
17944// --- command.dcli_ed..GetAnon
17945algo::strptr command::dcli_ed_GetAnon(command::dcli_ed &parent, i32 idx) {
17946    (void)parent;//only to avoid -Wunused-parameter
17947    switch(idx) {
17948        case(0): return strptr("target", 6);
17949        default: return algo::strptr();
17950    }
17951}
17952
17953// --- command.dcli_ed..NArgs
17954// Used with command lines
17955// Return # of command-line arguments that must follow this argument
17956// If FIELD is invalid, return -1
17957i32 command::dcli_ed_NArgs(command::FieldId field, algo::strptr& out_dflt, bool* out_anon) {
17958    i32 retval = 1;
17959    switch (field) {
17960        case command_FieldId_in: { // $comment
17961            *out_anon = false;
17962        } break;
17963        case command_FieldId_script_dir: { // $comment
17964            *out_anon = false;
17965        } break;
17966        case command_FieldId_target: { // $comment
17967            *out_anon = true;
17968        } break;
17969        case command_FieldId_dctr: { // $comment
17970            *out_anon = false;
17971        } break;
17972        case command_FieldId_dhost: { // $comment
17973            *out_anon = false;
17974        } break;
17975        case command_FieldId_dctrhost: { // $comment
17976            *out_anon = false;
17977        } break;
17978        case command_FieldId_create: { // $comment
17979            *out_anon = false;
17980            retval=0;
17981            out_dflt="Y";
17982        } break;
17983        case command_FieldId_cloneto: { // bool: no argument required but value may be specified as create:Y
17984            *out_anon = false;
17985        } break;
17986        case command_FieldId_renameto: { // bool: no argument required but value may be specified as create:Y
17987            *out_anon = false;
17988        } break;
17989        case command_FieldId_del: { // bool: no argument required but value may be specified as create:Y
17990            *out_anon = false;
17991            retval=0;
17992            out_dflt="Y";
17993        } break;
17994        case command_FieldId_purge: { // bool: no argument required but value may be specified as del:Y
17995            *out_anon = false;
17996            retval=0;
17997            out_dflt="Y";
17998        } break;
17999        case command_FieldId_write: { // bool: no argument required but value may be specified as purge:Y
18000            *out_anon = false;
18001            retval=0;
18002            out_dflt="Y";
18003        } break;
18004        case command_FieldId_force: { // bool: no argument required but value may be specified as write:Y
18005            *out_anon = false;
18006            retval=0;
18007            out_dflt="Y";
18008        } break;
18009        case command_FieldId_deep: { // bool: no argument required but value may be specified as force:Y
18010            *out_anon = false;
18011            retval=0;
18012            out_dflt="Y";
18013        } break;
18014        case command_FieldId_sync: { // bool: no argument required but value may be specified as deep:Y
18015            *out_anon = false;
18016            retval=0;
18017            out_dflt="Y";
18018        } break;
18019        case command_FieldId_keep_files: { // bool: no argument required but value may be specified as sync:Y
18020            *out_anon = false;
18021            retval=0;
18022            out_dflt="Y";
18023        } break;
18024        case command_FieldId_comment: { // bool: no argument required but value may be specified as keep_files:Y
18025            *out_anon = false;
18026        } break;
18027        case command_FieldId_dctr_from: { // bool: no argument required but value may be specified as keep_files:Y
18028            *out_anon = false;
18029        } break;
18030        case command_FieldId_dhost_ip: { // bool: no argument required but value may be specified as keep_files:Y
18031            *out_anon = false;
18032        } break;
18033        case command_FieldId_dhost_ext_ip: { // bool: no argument required but value may be specified as keep_files:Y
18034            *out_anon = false;
18035        } break;
18036        case command_FieldId_dhost_ext_port: { // bool: no argument required but value may be specified as keep_files:Y
18037            *out_anon = false;
18038        } break;
18039        case command_FieldId_dhost_user: { // bool: no argument required but value may be specified as keep_files:Y
18040            *out_anon = false;
18041        } break;
18042        default:
18043        retval=-1; // unrecognized
18044    }
18045    return retval;
18046}
18047
18048// --- command.dcli_ed_proc.dcli_ed.Start
18049// Start subprocess
18050// If subprocess already running, do nothing. Otherwise, start it
18051int command::dcli_ed_Start(command::dcli_ed_proc& parent) {
18052    int retval = 0;
18053    if (parent.pid == 0) {
18054        verblog(dcli_ed_ToCmdline(parent)); // maybe print command
18055#ifdef WIN32
18056        algo_lib::ResolveExecFname(parent.path);
18057        tempstr cmdline(dcli_ed_ToCmdline(parent));
18058        parent.pid = dospawn(Zeroterm(parent.path),Zeroterm(cmdline),parent.timeout,parent.fstdin,parent.fstdout,parent.fstderr);
18059#else
18060        parent.pid = fork();
18061        if (parent.pid == 0) { // child
18062            algo_lib::DieWithParent();
18063            if (parent.timeout > 0) {
18064                alarm(parent.timeout);
18065            }
18066            if (retval==0) retval=algo_lib::ApplyRedirect(parent.fstdin , 0);
18067            if (retval==0) retval=algo_lib::ApplyRedirect(parent.fstdout, 1);
18068            if (retval==0) retval=algo_lib::ApplyRedirect(parent.fstderr, 2);
18069            if (retval==0) retval= dcli_ed_Execv(parent);
18070            if (retval != 0) { // if start fails, print error
18071                int err=errno;
18072                prerr("command.dcli_ed_execv"
18073                <<Keyval("errno",err)
18074                <<Keyval("errstr",strerror(err))
18075                <<Keyval("comment","Execv failed"));
18076            }
18077            _exit(127); // if failed to start, exit anyway
18078        } else if (parent.pid == -1) {
18079            retval = errno; // failed to fork
18080        }
18081#endif
18082    }
18083    parent.status = parent.pid > 0 ? 0 : -1; // if didn't start, set error status
18084    return retval;
18085}
18086
18087// --- command.dcli_ed_proc.dcli_ed.StartRead
18088// Start subprocess & Read output
18089algo::Fildes command::dcli_ed_StartRead(command::dcli_ed_proc& parent, algo_lib::FFildes &read) {
18090    int pipefd[2];
18091    int rc=pipe(pipefd);
18092    (void)rc;
18093    read.fd.value = pipefd[0];
18094    parent.fstdout  << ">&" << pipefd[1];
18095    dcli_ed_Start(parent);
18096    (void)close(pipefd[1]);
18097    return read.fd;
18098}
18099
18100// --- command.dcli_ed_proc.dcli_ed.Kill
18101// Kill subprocess and wait
18102void command::dcli_ed_Kill(command::dcli_ed_proc& parent) {
18103    if (parent.pid != 0) {
18104        kill(parent.pid,9);
18105        dcli_ed_Wait(parent);
18106    }
18107}
18108
18109// --- command.dcli_ed_proc.dcli_ed.Wait
18110// Wait for subprocess to return
18111void command::dcli_ed_Wait(command::dcli_ed_proc& parent) {
18112    if (parent.pid > 0) {
18113        int wait_flags = 0;
18114        int wait_status = 0;
18115        int rc = -1;
18116        do {
18117            // really wait for subprocess to exit
18118            rc = waitpid(parent.pid,&wait_status,wait_flags);
18119        } while (rc==-1 && errno==EINTR);
18120        if (rc == parent.pid) {
18121            parent.status = wait_status;
18122            parent.pid = 0;
18123        }
18124    }
18125}
18126
18127// --- command.dcli_ed_proc.dcli_ed.Exec
18128// Start + Wait
18129// Execute subprocess and return exit code
18130int command::dcli_ed_Exec(command::dcli_ed_proc& parent) {
18131    dcli_ed_Start(parent);
18132    dcli_ed_Wait(parent);
18133    return parent.status;
18134}
18135
18136// --- command.dcli_ed_proc.dcli_ed.ExecX
18137// Start + Wait, throw exception on error
18138// Execute subprocess; throw human-readable exception on error
18139void command::dcli_ed_ExecX(command::dcli_ed_proc& parent) {
18140    int rc = dcli_ed_Exec(parent);
18141    vrfy(rc==0, tempstr() << "algo_lib.exec" << Keyval("cmd",dcli_ed_ToCmdline(parent))
18142    << Keyval("comment",algo::DescribeWaitStatus(parent.status)));
18143}
18144
18145// --- command.dcli_ed_proc.dcli_ed.Execv
18146// Call execv()
18147// Call execv with specified parameters
18148int command::dcli_ed_Execv(command::dcli_ed_proc& parent) {
18149    int ret = 0;
18150    algo::StringAry args;
18151    dcli_ed_ToArgv(parent, args);
18152    char **argv = (char**)alloca((ary_N(args)+1)*sizeof(*argv));
18153    ind_beg(algo::StringAry_ary_curs,arg,args) {
18154        argv[ind_curs(arg).index] = Zeroterm(arg);
18155    }ind_end;
18156    argv[ary_N(args)] = NULL;
18157    // if parent.path is relative, search for it in PATH
18158    algo_lib::ResolveExecFname(parent.path);
18159    ret = execv(Zeroterm(parent.path),argv);
18160    return ret;
18161}
18162
18163// --- command.dcli_ed_proc.dcli_ed.ToCmdline
18164algo::tempstr command::dcli_ed_ToCmdline(command::dcli_ed_proc& parent) {
18165    algo::tempstr retval;
18166    retval << parent.path << " ";
18167    command::dcli_ed_PrintArgv(parent.cmd,retval);
18168    if (ch_N(parent.fstdin)) {
18169        retval << " " << parent.fstdin;
18170    }
18171    if (ch_N(parent.fstdout)) {
18172        retval << " " << parent.fstdout;
18173    }
18174    if (ch_N(parent.fstderr)) {
18175        retval << " 2" << parent.fstderr;
18176    }
18177    return retval;
18178}
18179
18180// --- command.dcli_ed_proc.dcli_ed.ToArgv
18181// Form array from the command line
18182void command::dcli_ed_ToArgv(command::dcli_ed_proc& parent, algo::StringAry& args) {
18183    ary_RemoveAll(args);
18184    ary_Alloc(args) << parent.path;
18185
18186    if (parent.cmd.in != "data") {
18187        cstring *arg = &ary_Alloc(args);
18188        *arg << "-in:";
18189        cstring_Print(parent.cmd.in, *arg);
18190    }
18191
18192    if (parent.cmd.script_dir != "docker") {
18193        cstring *arg = &ary_Alloc(args);
18194        *arg << "-script_dir:";
18195        cstring_Print(parent.cmd.script_dir, *arg);
18196    }
18197
18198    if (parent.cmd.target != "") {
18199        cstring *arg = &ary_Alloc(args);
18200        *arg << "-target:";
18201        Smallstr50_Print(parent.cmd.target, *arg);
18202    }
18203
18204    if (parent.cmd.dctr != "") {
18205        cstring *arg = &ary_Alloc(args);
18206        *arg << "-dctr:";
18207        Smallstr50_Print(parent.cmd.dctr, *arg);
18208    }
18209
18210    if (parent.cmd.dhost != "") {
18211        cstring *arg = &ary_Alloc(args);
18212        *arg << "-dhost:";
18213        Smallstr50_Print(parent.cmd.dhost, *arg);
18214    }
18215
18216    if (parent.cmd.dctrhost != "") {
18217        cstring *arg = &ary_Alloc(args);
18218        *arg << "-dctrhost:";
18219        Smallstr50_Print(parent.cmd.dctrhost, *arg);
18220    }
18221
18222    if (parent.cmd.create != false) {
18223        cstring *arg = &ary_Alloc(args);
18224        *arg << "-create:";
18225        bool_Print(parent.cmd.create, *arg);
18226    }
18227
18228    if (parent.cmd.cloneto != "") {
18229        cstring *arg = &ary_Alloc(args);
18230        *arg << "-cloneto:";
18231        Smallstr50_Print(parent.cmd.cloneto, *arg);
18232    }
18233
18234    if (parent.cmd.renameto != "") {
18235        cstring *arg = &ary_Alloc(args);
18236        *arg << "-renameto:";
18237        Smallstr50_Print(parent.cmd.renameto, *arg);
18238    }
18239
18240    if (parent.cmd.del != false) {
18241        cstring *arg = &ary_Alloc(args);
18242        *arg << "-del:";
18243        bool_Print(parent.cmd.del, *arg);
18244    }
18245
18246    if (parent.cmd.purge != false) {
18247        cstring *arg = &ary_Alloc(args);
18248        *arg << "-purge:";
18249        bool_Print(parent.cmd.purge, *arg);
18250    }
18251
18252    if (parent.cmd.write != false) {
18253        cstring *arg = &ary_Alloc(args);
18254        *arg << "-write:";
18255        bool_Print(parent.cmd.write, *arg);
18256    }
18257
18258    if (parent.cmd.force != false) {
18259        cstring *arg = &ary_Alloc(args);
18260        *arg << "-force:";
18261        bool_Print(parent.cmd.force, *arg);
18262    }
18263
18264    if (parent.cmd.deep != false) {
18265        cstring *arg = &ary_Alloc(args);
18266        *arg << "-deep:";
18267        bool_Print(parent.cmd.deep, *arg);
18268    }
18269
18270    if (parent.cmd.sync != true) {
18271        cstring *arg = &ary_Alloc(args);
18272        *arg << "-sync:";
18273        bool_Print(parent.cmd.sync, *arg);
18274    }
18275
18276    if (parent.cmd.keep_files != false) {
18277        cstring *arg = &ary_Alloc(args);
18278        *arg << "-keep_files:";
18279        bool_Print(parent.cmd.keep_files, *arg);
18280    }
18281
18282    if (parent.cmd.comment != "") {
18283        cstring *arg = &ary_Alloc(args);
18284        *arg << "-comment:";
18285        Comment_Print(parent.cmd.comment, *arg);
18286    }
18287
18288    if (parent.cmd.dctr_from != "ubuntu") {
18289        cstring *arg = &ary_Alloc(args);
18290        *arg << "-dctr_from:";
18291        cstring_Print(parent.cmd.dctr_from, *arg);
18292    }
18293
18294    if (parent.cmd.dhost_ip != "") {
18295        cstring *arg = &ary_Alloc(args);
18296        *arg << "-dhost_ip:";
18297        cstring_Print(parent.cmd.dhost_ip, *arg);
18298    }
18299
18300    if (parent.cmd.dhost_ext_ip != "") {
18301        cstring *arg = &ary_Alloc(args);
18302        *arg << "-dhost_ext_ip:";
18303        cstring_Print(parent.cmd.dhost_ext_ip, *arg);
18304    }
18305
18306    if (parent.cmd.dhost_ext_port != "3022") {
18307        cstring *arg = &ary_Alloc(args);
18308        *arg << "-dhost_ext_port:";
18309        cstring_Print(parent.cmd.dhost_ext_port, *arg);
18310    }
18311
18312    if (parent.cmd.dhost_user != "dcliusr") {
18313        cstring *arg = &ary_Alloc(args);
18314        *arg << "-dhost_user:";
18315        cstring_Print(parent.cmd.dhost_user, *arg);
18316    }
18317    for (int i=1; i < algo_lib::_db.cmdline.verbose; ++i) {
18318        ary_Alloc(args) << "-verbose";
18319    }
18320}
18321
18322// --- command.dcli_ed_proc..Uninit
18323void command::dcli_ed_proc_Uninit(command::dcli_ed_proc& parent) {
18324    command::dcli_ed_proc &row = parent; (void)row;
18325
18326    // command.dcli_ed_proc.dcli_ed.Uninit (Exec)  //
18327    dcli_ed_Kill(parent); // kill child, ensure forward progress
18328}
18329
18330// --- command.dcli_proc.dcli.Start
18331// Start subprocess
18332// If subprocess already running, do nothing. Otherwise, start it
18333int command::dcli_Start(command::dcli_proc& parent) {
18334    int retval = 0;
18335    if (parent.pid == 0) {
18336        verblog(dcli_ToCmdline(parent)); // maybe print command
18337#ifdef WIN32
18338        algo_lib::ResolveExecFname(parent.path);
18339        tempstr cmdline(dcli_ToCmdline(parent));
18340        parent.pid = dospawn(Zeroterm(parent.path),Zeroterm(cmdline),parent.timeout,parent.fstdin,parent.fstdout,parent.fstderr);
18341#else
18342        parent.pid = fork();
18343        if (parent.pid == 0) { // child
18344            algo_lib::DieWithParent();
18345            if (parent.timeout > 0) {
18346                alarm(parent.timeout);
18347            }
18348            if (retval==0) retval=algo_lib::ApplyRedirect(parent.fstdin , 0);
18349            if (retval==0) retval=algo_lib::ApplyRedirect(parent.fstdout, 1);
18350            if (retval==0) retval=algo_lib::ApplyRedirect(parent.fstderr, 2);
18351            if (retval==0) retval= dcli_Execv(parent);
18352            if (retval != 0) { // if start fails, print error
18353                int err=errno;
18354                prerr("command.dcli_execv"
18355                <<Keyval("errno",err)
18356                <<Keyval("errstr",strerror(err))
18357                <<Keyval("comment","Execv failed"));
18358            }
18359            _exit(127); // if failed to start, exit anyway
18360        } else if (parent.pid == -1) {
18361            retval = errno; // failed to fork
18362        }
18363#endif
18364    }
18365    parent.status = parent.pid > 0 ? 0 : -1; // if didn't start, set error status
18366    return retval;
18367}
18368
18369// --- command.dcli_proc.dcli.StartRead
18370// Start subprocess & Read output
18371algo::Fildes command::dcli_StartRead(command::dcli_proc& parent, algo_lib::FFildes &read) {
18372    int pipefd[2];
18373    int rc=pipe(pipefd);
18374    (void)rc;
18375    read.fd.value = pipefd[0];
18376    parent.fstdout  << ">&" << pipefd[1];
18377    dcli_Start(parent);
18378    (void)close(pipefd[1]);
18379    return read.fd;
18380}
18381
18382// --- command.dcli_proc.dcli.Kill
18383// Kill subprocess and wait
18384void command::dcli_Kill(command::dcli_proc& parent) {
18385    if (parent.pid != 0) {
18386        kill(parent.pid,9);
18387        dcli_Wait(parent);
18388    }
18389}
18390
18391// --- command.dcli_proc.dcli.Wait
18392// Wait for subprocess to return
18393void command::dcli_Wait(command::dcli_proc& parent) {
18394    if (parent.pid > 0) {
18395        int wait_flags = 0;
18396        int wait_status = 0;
18397        int rc = -1;
18398        do {
18399            // really wait for subprocess to exit
18400            rc = waitpid(parent.pid,&wait_status,wait_flags);
18401        } while (rc==-1 && errno==EINTR);
18402        if (rc == parent.pid) {
18403            parent.status = wait_status;
18404            parent.pid = 0;
18405        }
18406    }
18407}
18408
18409// --- command.dcli_proc.dcli.Exec
18410// Start + Wait
18411// Execute subprocess and return exit code
18412int command::dcli_Exec(command::dcli_proc& parent) {
18413    dcli_Start(parent);
18414    dcli_Wait(parent);
18415    return parent.status;
18416}
18417
18418// --- command.dcli_proc.dcli.ExecX
18419// Start + Wait, throw exception on error
18420// Execute subprocess; throw human-readable exception on error
18421void command::dcli_ExecX(command::dcli_proc& parent) {
18422    int rc = dcli_Exec(parent);
18423    vrfy(rc==0, tempstr() << "algo_lib.exec" << Keyval("cmd",dcli_ToCmdline(parent))
18424    << Keyval("comment",algo::DescribeWaitStatus(parent.status)));
18425}
18426
18427// --- command.dcli_proc.dcli.Execv
18428// Call execv()
18429// Call execv with specified parameters
18430int command::dcli_Execv(command::dcli_proc& parent) {
18431    int ret = 0;
18432    algo::StringAry args;
18433    dcli_ToArgv(parent, args);
18434    char **argv = (char**)alloca((ary_N(args)+1)*sizeof(*argv));
18435    ind_beg(algo::StringAry_ary_curs,arg,args) {
18436        argv[ind_curs(arg).index] = Zeroterm(arg);
18437    }ind_end;
18438    argv[ary_N(args)] = NULL;
18439    // if parent.path is relative, search for it in PATH
18440    algo_lib::ResolveExecFname(parent.path);
18441    ret = execv(Zeroterm(parent.path),argv);
18442    return ret;
18443}
18444
18445// --- command.dcli_proc.dcli.ToCmdline
18446algo::tempstr command::dcli_ToCmdline(command::dcli_proc& parent) {
18447    algo::tempstr retval;
18448    retval << parent.path << " ";
18449    command::dcli_PrintArgv(parent.cmd,retval);
18450    if (ch_N(parent.fstdin)) {
18451        retval << " " << parent.fstdin;
18452    }
18453    if (ch_N(parent.fstdout)) {
18454        retval << " " << parent.fstdout;
18455    }
18456    if (ch_N(parent.fstderr)) {
18457        retval << " 2" << parent.fstderr;
18458    }
18459    return retval;
18460}
18461
18462// --- command.dcli_proc.dcli.ToArgv
18463// Form array from the command line
18464void command::dcli_ToArgv(command::dcli_proc& parent, algo::StringAry& args) {
18465    ary_RemoveAll(args);
18466    ary_Alloc(args) << parent.path;
18467
18468    if (parent.cmd.in != "data") {
18469        cstring *arg = &ary_Alloc(args);
18470        *arg << "-in:";
18471        cstring_Print(parent.cmd.in, *arg);
18472    }
18473
18474    if (parent.cmd.cmd != "") {
18475        cstring *arg = &ary_Alloc(args);
18476        *arg << "-cmd:";
18477        cstring_Print(parent.cmd.cmd, *arg);
18478    }
18479
18480    if (parent.cmd.cmd_dctr != "") {
18481        cstring *arg = &ary_Alloc(args);
18482        *arg << "-cmd_dctr:";
18483        cstring_Print(parent.cmd.cmd_dctr, *arg);
18484    }
18485
18486    if (parent.cmd.config_ssh != false) {
18487        cstring *arg = &ary_Alloc(args);
18488        *arg << "-config_ssh:";
18489        bool_Print(parent.cmd.config_ssh, *arg);
18490    }
18491
18492    if (parent.cmd.config_ssh_vpn != false) {
18493        cstring *arg = &ary_Alloc(args);
18494        *arg << "-config_ssh_vpn:";
18495        bool_Print(parent.cmd.config_ssh_vpn, *arg);
18496    }
18497
18498    if (parent.cmd.clean_run != false) {
18499        cstring *arg = &ary_Alloc(args);
18500        *arg << "-clean_run:";
18501        bool_Print(parent.cmd.clean_run, *arg);
18502    }
18503
18504    if (parent.cmd.shell != false) {
18505        cstring *arg = &ary_Alloc(args);
18506        *arg << "-shell:";
18507        bool_Print(parent.cmd.shell, *arg);
18508    }
18509
18510    if (parent.cmd.inspect != false) {
18511        cstring *arg = &ary_Alloc(args);
18512        *arg << "-inspect:";
18513        bool_Print(parent.cmd.inspect, *arg);
18514    }
18515
18516    if (parent.cmd.logs != false) {
18517        cstring *arg = &ary_Alloc(args);
18518        *arg << "-logs:";
18519        bool_Print(parent.cmd.logs, *arg);
18520    }
18521
18522    if (parent.cmd.generate_build != false) {
18523        cstring *arg = &ary_Alloc(args);
18524        *arg << "-generate_build:";
18525        bool_Print(parent.cmd.generate_build, *arg);
18526    }
18527
18528    if (parent.cmd.build != false) {
18529        cstring *arg = &ary_Alloc(args);
18530        *arg << "-build:";
18531        bool_Print(parent.cmd.build, *arg);
18532    }
18533
18534    if (parent.cmd.dhost.expr != "") {
18535        cstring *arg = &ary_Alloc(args);
18536        *arg << "-dhost:";
18537        command::dhost_Print(parent.cmd, *arg);
18538    }
18539
18540    if (parent.cmd.dctr.expr != "") {
18541        cstring *arg = &ary_Alloc(args);
18542        *arg << "-dctr:";
18543        command::dctr_Print(parent.cmd, *arg);
18544    }
18545
18546    if (parent.cmd.dctrhost.expr != "") {
18547        cstring *arg = &ary_Alloc(args);
18548        *arg << "-dctrhost:";
18549        command::dctrhost_Print(parent.cmd, *arg);
18550    }
18551
18552    if (parent.cmd.t != false) {
18553        cstring *arg = &ary_Alloc(args);
18554        *arg << "-t:";
18555        bool_Print(parent.cmd.t, *arg);
18556    }
18557
18558    if (parent.cmd.q != false) {
18559        cstring *arg = &ary_Alloc(args);
18560        *arg << "-q:";
18561        bool_Print(parent.cmd.q, *arg);
18562    }
18563
18564    if (parent.cmd.dry_run != false) {
18565        cstring *arg = &ary_Alloc(args);
18566        *arg << "-dry_run:";
18567        bool_Print(parent.cmd.dry_run, *arg);
18568    }
18569    for (int i=1; i < algo_lib::_db.cmdline.verbose; ++i) {
18570        ary_Alloc(args) << "-verbose";
18571    }
18572}
18573
18574// --- command.dcli_proc..Uninit
18575void command::dcli_proc_Uninit(command::dcli_proc& parent) {
18576    command::dcli_proc &row = parent; (void)row;
18577
18578    // command.dcli_proc.dcli.Uninit (Exec)  //
18579    dcli_Kill(parent); // kill child, ensure forward progress
18580}
18581
18582// --- command.fast.enc.ToCstr
18583// Convert numeric value of field to one of predefined string constants.
18584// If string is found, return a static C string. Otherwise, return NULL.
18585const char* command::enc_ToCstr(const command::fast& parent) {
18586    const char *ret = NULL;
18587    switch(enc_GetEnum(parent)) {
18588        case command_fast_enc_none         : ret = "none";  break;
18589        case command_fast_enc_unsigned     : ret = "unsigned";  break;
18590        case command_fast_enc_signed       : ret = "signed";  break;
18591        case command_fast_enc_string       : ret = "string";  break;
18592        case command_fast_enc_bytevector   : ret = "bytevector";  break;
18593        case command_fast_enc_scaled       : ret = "scaled";  break;
18594        case command_fast_enc_pmap         : ret = "pmap";  break;
18595    }
18596    return ret;
18597}
18598
18599// --- command.fast.enc.Print
18600// Convert enc to a string. First, attempt conversion to a known string.
18601// If no string matches, print enc as a numeric value.
18602void command::enc_Print(const command::fast& parent, algo::cstring &lhs) {
18603    const char *strval = enc_ToCstr(parent);
18604    if (strval) {
18605        lhs << strval;
18606    } else {
18607        lhs << parent.enc;
18608    }
18609}
18610
18611// --- command.fast.enc.SetStrptrMaybe
18612// Convert string to field.
18613// If the string is invalid, do not modify field and return false.
18614// In case of success, return true
18615bool command::enc_SetStrptrMaybe(command::fast& parent, algo::strptr rhs) {
18616    bool ret = false;
18617    switch (elems_N(rhs)) {
18618        case 4: {
18619            switch (u64(algo::ReadLE32(rhs.elems))) {
18620                case LE_STR4('n','o','n','e'): {
18621                    enc_SetEnum(parent,command_fast_enc_none); ret = true; break;
18622                }
18623                case LE_STR4('p','m','a','p'): {
18624                    enc_SetEnum(parent,command_fast_enc_pmap); ret = true; break;
18625                }
18626            }
18627            break;
18628        }
18629        case 6: {
18630            switch (u64(algo::ReadLE32(rhs.elems))|(u64(algo::ReadLE16(rhs.elems+4))<<32)) {
18631                case LE_STR6('s','c','a','l','e','d'): {
18632                    enc_SetEnum(parent,command_fast_enc_scaled); ret = true; break;
18633                }
18634                case LE_STR6('s','i','g','n','e','d'): {
18635                    enc_SetEnum(parent,command_fast_enc_signed); ret = true; break;
18636                }
18637                case LE_STR6('s','t','r','i','n','g'): {
18638                    enc_SetEnum(parent,command_fast_enc_string); ret = true; break;
18639                }
18640            }
18641            break;
18642        }
18643        case 8: {
18644            switch (algo::ReadLE64(rhs.elems)) {
18645                case LE_STR8('u','n','s','i','g','n','e','d'): {
18646                    enc_SetEnum(parent,command_fast_enc_unsigned); ret = true; break;
18647                }
18648            }
18649            break;
18650        }
18651        case 10: {
18652            switch (algo::ReadLE64(rhs.elems)) {
18653                case LE_STR8('b','y','t','e','v','e','c','t'): {
18654                    if (memcmp(rhs.elems+8,"or",2)==0) { enc_SetEnum(parent,command_fast_enc_bytevector); ret = true; break; }
18655                    break;
18656                }
18657            }
18658            break;
18659        }
18660    }
18661    return ret;
18662}
18663
18664// --- command.fast.enc.SetStrptr
18665// Convert string to field.
18666// If the string is invalid, set numeric value to DFLT
18667void command::enc_SetStrptr(command::fast& parent, algo::strptr rhs, command_fast_enc_Enum dflt) {
18668    if (!enc_SetStrptrMaybe(parent,rhs)) enc_SetEnum(parent,dflt);
18669}
18670
18671// --- command.fast.enc.ReadStrptrMaybe
18672// Convert string to field. Return success value
18673bool command::enc_ReadStrptrMaybe(command::fast& parent, algo::strptr rhs) {
18674    bool retval = false;
18675    retval = enc_SetStrptrMaybe(parent,rhs); // try symbol conversion
18676    if (!retval) { // didn't work? try reading as underlying type
18677        retval = u8_ReadStrptrMaybe(parent.enc,rhs);
18678    }
18679    return retval;
18680}
18681
18682// --- command.fast.ifmt.ToCstr
18683// Convert numeric value of field to one of predefined string constants.
18684// If string is found, return a static C string. Otherwise, return NULL.
18685const char* command::ifmt_ToCstr(const command::fast& parent) {
18686    const char *ret = NULL;
18687    switch(ifmt_GetEnum(parent)) {
18688        case command_fast_ifmt_none        : ret = "none";  break;
18689        case command_fast_ifmt_fast        : ret = "fast";  break;
18690        case command_fast_ifmt_ssim        : ret = "ssim";  break;
18691        case command_fast_ifmt_xetradump   : ret = "xetradump";  break;
18692    }
18693    return ret;
18694}
18695
18696// --- command.fast.ifmt.Print
18697// Convert ifmt to a string. First, attempt conversion to a known string.
18698// If no string matches, print ifmt as a numeric value.
18699void command::ifmt_Print(const command::fast& parent, algo::cstring &lhs) {
18700    const char *strval = ifmt_ToCstr(parent);
18701    if (strval) {
18702        lhs << strval;
18703    } else {
18704        lhs << parent.ifmt;
18705    }
18706}
18707
18708// --- command.fast.ifmt.SetStrptrMaybe
18709// Convert string to field.
18710// If the string is invalid, do not modify field and return false.
18711// In case of success, return true
18712bool command::ifmt_SetStrptrMaybe(command::fast& parent, algo::strptr rhs) {
18713    bool ret = false;
18714    switch (elems_N(rhs)) {
18715        case 4: {
18716            switch (u64(algo::ReadLE32(rhs.elems))) {
18717                case LE_STR4('f','a','s','t'): {
18718                    ifmt_SetEnum(parent,command_fast_ifmt_fast); ret = true; break;
18719                }
18720                case LE_STR4('n','o','n','e'): {
18721                    ifmt_SetEnum(parent,command_fast_ifmt_none); ret = true; break;
18722                }
18723                case LE_STR4('s','s','i','m'): {
18724                    ifmt_SetEnum(parent,command_fast_ifmt_ssim); ret = true; break;
18725                }
18726            }
18727            break;
18728        }
18729        case 9: {
18730            switch (algo::ReadLE64(rhs.elems)) {
18731                case LE_STR8('x','e','t','r','a','d','u','m'): {
18732                    if (memcmp(rhs.elems+8,"p",1)==0) { ifmt_SetEnum(parent,command_fast_ifmt_xetradump); ret = true; break; }
18733                    break;
18734                }
18735            }
18736            break;
18737        }
18738    }
18739    return ret;
18740}
18741
18742// --- command.fast.ifmt.SetStrptr
18743// Convert string to field.
18744// If the string is invalid, set numeric value to DFLT
18745void command::ifmt_SetStrptr(command::fast& parent, algo::strptr rhs, command_fast_ifmt_Enum dflt) {
18746    if (!ifmt_SetStrptrMaybe(parent,rhs)) ifmt_SetEnum(parent,dflt);
18747}
18748
18749// --- command.fast.ifmt.ReadStrptrMaybe
18750// Convert string to field. Return success value
18751bool command::ifmt_ReadStrptrMaybe(command::fast& parent, algo::strptr rhs) {
18752    bool retval = false;
18753    retval = ifmt_SetStrptrMaybe(parent,rhs); // try symbol conversion
18754    if (!retval) { // didn't work? try reading as underlying type
18755        retval = u8_ReadStrptrMaybe(parent.ifmt,rhs);
18756    }
18757    return retval;
18758}
18759
18760// --- command.fast.ofmt.ToCstr
18761// Convert numeric value of field to one of predefined string constants.
18762// If string is found, return a static C string. Otherwise, return NULL.
18763const char* command::ofmt_ToCstr(const command::fast& parent) {
18764    const char *ret = NULL;
18765    switch(ofmt_GetEnum(parent)) {
18766        case command_fast_ofmt_none        : ret = "none";  break;
18767        case command_fast_ofmt_fast        : ret = "fast";  break;
18768        case command_fast_ofmt_ssim        : ret = "ssim";  break;
18769        case command_fast_ofmt_tagvalue    : ret = "tagvalue";  break;
18770    }
18771    return ret;
18772}
18773
18774// --- command.fast.ofmt.Print
18775// Convert ofmt to a string. First, attempt conversion to a known string.
18776// If no string matches, print ofmt as a numeric value.
18777void command::ofmt_Print(const command::fast& parent, algo::cstring &lhs) {
18778    const char *strval = ofmt_ToCstr(parent);
18779    if (strval) {
18780        lhs << strval;
18781    } else {
18782        lhs << parent.ofmt;
18783    }
18784}
18785
18786// --- command.fast.ofmt.SetStrptrMaybe
18787// Convert string to field.
18788// If the string is invalid, do not modify field and return false.
18789// In case of success, return true
18790bool command::ofmt_SetStrptrMaybe(command::fast& parent, algo::strptr rhs) {
18791    bool ret = false;
18792    switch (elems_N(rhs)) {
18793        case 4: {
18794            switch (u64(algo::ReadLE32(rhs.elems))) {
18795                case LE_STR4('f','a','s','t'): {
18796                    ofmt_SetEnum(parent,command_fast_ofmt_fast); ret = true; break;
18797                }
18798                case LE_STR4('n','o','n','e'): {
18799                    ofmt_SetEnum(parent,command_fast_ofmt_none); ret = true; break;
18800                }
18801                case LE_STR4('s','s','i','m'): {
18802                    ofmt_SetEnum(parent,command_fast_ofmt_ssim); ret = true; break;
18803                }
18804            }
18805            break;
18806        }
18807        case 8: {
18808            switch (algo::ReadLE64(rhs.elems)) {
18809                case LE_STR8('t','a','g','v','a','l','u','e'): {
18810                    ofmt_SetEnum(parent,command_fast_ofmt_tagvalue); ret = true; break;
18811                }
18812            }
18813            break;
18814        }
18815    }
18816    return ret;
18817}
18818
18819// --- command.fast.ofmt.SetStrptr
18820// Convert string to field.
18821// If the string is invalid, set numeric value to DFLT
18822void command::ofmt_SetStrptr(command::fast& parent, algo::strptr rhs, command_fast_ofmt_Enum dflt) {
18823    if (!ofmt_SetStrptrMaybe(parent,rhs)) ofmt_SetEnum(parent,dflt);
18824}
18825
18826// --- command.fast.ofmt.ReadStrptrMaybe
18827// Convert string to field. Return success value
18828bool command::ofmt_ReadStrptrMaybe(command::fast& parent, algo::strptr rhs) {
18829    bool retval = false;
18830    retval = ofmt_SetStrptrMaybe(parent,rhs); // try symbol conversion
18831    if (!retval) { // didn't work? try reading as underlying type
18832        retval = u8_ReadStrptrMaybe(parent.ofmt,rhs);
18833    }
18834    return retval;
18835}
18836
18837// --- command.fast..ReadFieldMaybe
18838bool command::fast_ReadFieldMaybe(command::fast& parent, algo::strptr field, algo::strptr strval) {
18839    bool retval = true;
18840    command::FieldId field_id;
18841    (void)value_SetStrptrMaybe(field_id,field);
18842    switch(field_id) {
18843        case command_FieldId_in: {
18844            retval = algo::cstring_ReadStrptrMaybe(parent.in, strval);
18845            break;
18846        }
18847        case command_FieldId_genamc: {
18848            retval = bool_ReadStrptrMaybe(parent.genamc, strval);
18849            break;
18850        }
18851        case command_FieldId_ns: {
18852            retval = algo::Smallstr16_ReadStrptrMaybe(parent.ns, strval);
18853            break;
18854        }
18855        case command_FieldId_xml: {
18856            retval = algo::cstring_ReadStrptrMaybe(parent.xml, strval);
18857            break;
18858        }
18859        case command_FieldId_add_reset: {
18860            retval = bool_ReadStrptrMaybe(parent.add_reset, strval);
18861            break;
18862        }
18863        case command_FieldId_pretty: {
18864            retval = bool_ReadStrptrMaybe(parent.pretty, strval);
18865            break;
18866        }
18867        case command_FieldId_write: {
18868            retval = bool_ReadStrptrMaybe(parent.write, strval);
18869            break;
18870        }
18871        case command_FieldId_encode: {
18872            retval = bool_ReadStrptrMaybe(parent.encode, strval);
18873            break;
18874        }
18875        case command_FieldId_decode: {
18876            retval = bool_ReadStrptrMaybe(parent.decode, strval);
18877            break;
18878        }
18879        case command_FieldId_enc: {
18880            retval = enc_ReadStrptrMaybe(parent, strval);
18881            break;
18882        }
18883        case command_FieldId_nullable: {
18884            retval = bool_ReadStrptrMaybe(parent.nullable, strval);
18885            break;
18886        }
18887        case command_FieldId_ifmt: {
18888            retval = ifmt_ReadStrptrMaybe(parent, strval);
18889            break;
18890        }
18891        case command_FieldId_ofmt: {
18892            retval = ofmt_ReadStrptrMaybe(parent, strval);
18893            break;
18894        }
18895        case command_FieldId_echo: {
18896            retval = bool_ReadStrptrMaybe(parent.echo, strval);
18897            break;
18898        }
18899        case command_FieldId_hex: {
18900            retval = bool_ReadStrptrMaybe(parent.hex, strval);
18901            break;
18902        }
18903        case command_FieldId_fix_bs: {
18904            retval = algo::Smallstr10_ReadStrptrMaybe(parent.fix_bs, strval);
18905            break;
18906        }
18907        case command_FieldId_fix_soh: {
18908            retval = char_ReadStrptrMaybe(parent.fix_soh, strval);
18909            break;
18910        }
18911        case command_FieldId_fix_nl: {
18912            retval = bool_ReadStrptrMaybe(parent.fix_nl, strval);
18913            break;
18914        }
18915        case command_FieldId_fast_msg_max: {
18916            retval = i32_ReadStrptrMaybe(parent.fast_msg_max, strval);
18917            break;
18918        }
18919        default: break;
18920    }
18921    if (!retval) {
18922        algo_lib::AppendErrtext("attr",field);
18923    }
18924    return retval;
18925}
18926
18927// --- command.fast..ReadTupleMaybe
18928// Read fields of command::fast from attributes of ascii tuple TUPLE
18929bool command::fast_ReadTupleMaybe(command::fast &parent, algo::Tuple &tuple) {
18930    bool retval = true;
18931    ind_beg(algo::Tuple_attrs_curs,attr,tuple) {
18932        retval = fast_ReadFieldMaybe(parent, attr.name, attr.value);
18933        if (!retval) {
18934            break;
18935        }
18936    }ind_end;
18937    return retval;
18938}
18939
18940// --- command.fast..Init
18941// Set all fields to initial values.
18942void command::fast_Init(command::fast& parent) {
18943    parent.in = algo::strptr("data");
18944    parent.genamc = bool(false);
18945    parent.ns = algo::strptr("");
18946    parent.xml = algo::strptr("");
18947    parent.add_reset = bool(true);
18948    parent.pretty = bool(false);
18949    parent.write = bool(false);
18950    parent.encode = bool(false);
18951    parent.decode = bool(false);
18952    parent.enc = u8(0);
18953    parent.nullable = bool(false);
18954    parent.ifmt = u8(0);
18955    parent.ofmt = u8(0);
18956    parent.echo = bool(false);
18957    parent.hex = bool(false);
18958    parent.fix_bs = algo::strptr("FIX.4.2");
18959    parent.fix_soh = char('|');
18960    parent.fix_nl = bool(true);
18961    parent.fast_msg_max = i32(1024);
18962}
18963
18964// --- command.fast..ToCmdline
18965// Convenience function that returns a full command line
18966// Assume command is in a directory called bin
18967tempstr command::fast_ToCmdline(command::fast& row) {
18968    tempstr ret;
18969    ret << "bin/fast ";
18970    fast_PrintArgv(row, ret);
18971    // inherit less intense verbose, debug options
18972    for (int i = 1; i < algo_lib::_db.cmdline.verbose; i++) {
18973        ret << " -verbose";
18974    }
18975    for (int i = 1; i < algo_lib::_db.cmdline.debug; i++) {
18976        ret << " -debug";
18977    }
18978    return ret;
18979}
18980
18981// --- command.fast..PrintArgv
18982// print string representation of ROW to string STR
18983// cfmt:command.fast.Argv  printfmt:Tuple
18984void command::fast_PrintArgv(command::fast& row, algo::cstring& str) {
18985    algo::tempstr temp;
18986    (void)temp;
18987    (void)str;
18988    if (!(row.in == "data")) {
18989        ch_RemoveAll(temp);
18990        cstring_Print(row.in, temp);
18991        str << " -in:";
18992        strptr_PrintBash(temp,str);
18993    }
18994    if (!(row.genamc == false)) {
18995        ch_RemoveAll(temp);
18996        bool_Print(row.genamc, temp);
18997        str << " -genamc:";
18998        strptr_PrintBash(temp,str);
18999    }
19000    if (!(row.ns == "")) {
19001        ch_RemoveAll(temp);
19002        Smallstr16_Print(row.ns, temp);
19003        str << " -ns:";
19004        strptr_PrintBash(temp,str);
19005    }
19006    if (!(row.xml == "")) {
19007        ch_RemoveAll(temp);
19008        cstring_Print(row.xml, temp);
19009        str << " -xml:";
19010        strptr_PrintBash(temp,str);
19011    }
19012    if (!(row.add_reset == true)) {
19013        ch_RemoveAll(temp);
19014        bool_Print(row.add_reset, temp);
19015        str << " -add_reset:";
19016        strptr_PrintBash(temp,str);
19017    }
19018    if (!(row.pretty == false)) {
19019        ch_RemoveAll(temp);
19020        bool_Print(row.pretty, temp);
19021        str << " -pretty:";
19022        strptr_PrintBash(temp,str);
19023    }
19024    if (!(row.write == false)) {
19025        ch_RemoveAll(temp);
19026        bool_Print(row.write, temp);
19027        str << " -write:";
19028        strptr_PrintBash(temp,str);
19029    }
19030    if (!(row.encode == false)) {
19031        ch_RemoveAll(temp);
19032        bool_Print(row.encode, temp);
19033        str << " -encode:";
19034        strptr_PrintBash(temp,str);
19035    }
19036    if (!(row.decode == false)) {
19037        ch_RemoveAll(temp);
19038        bool_Print(row.decode, temp);
19039        str << " -decode:";
19040        strptr_PrintBash(temp,str);
19041    }
19042    if (!(row.enc == 0)) {
19043        ch_RemoveAll(temp);
19044        command::enc_Print(const_cast<command::fast&>(row), temp);
19045        str << " -enc:";
19046        strptr_PrintBash(temp,str);
19047    }
19048    if (!(row.nullable == false)) {
19049        ch_RemoveAll(temp);
19050        bool_Print(row.nullable, temp);
19051        str << " -nullable:";
19052        strptr_PrintBash(temp,str);
19053    }
19054    if (!(row.ifmt == 0)) {
19055        ch_RemoveAll(temp);
19056        command::ifmt_Print(const_cast<command::fast&>(row), temp);
19057        str << " -ifmt:";
19058        strptr_PrintBash(temp,str);
19059    }
19060    if (!(row.ofmt == 0)) {
19061        ch_RemoveAll(temp);
19062        command::ofmt_Print(const_cast<command::fast&>(row), temp);
19063        str << " -ofmt:";
19064        strptr_PrintBash(temp,str);
19065    }
19066    if (!(row.echo == false)) {
19067        ch_RemoveAll(temp);
19068        bool_Print(row.echo, temp);
19069        str << " -echo:";
19070        strptr_PrintBash(temp,str);
19071    }
19072    if (!(row.hex == false)) {
19073        ch_RemoveAll(temp);
19074        bool_Print(row.hex, temp);
19075        str << " -hex:";
19076        strptr_PrintBash(temp,str);
19077    }
19078    if (!(row.fix_bs == "FIX.4.2")) {
19079        ch_RemoveAll(temp);
19080        Smallstr10_Print(row.fix_bs, temp);
19081        str << " -fix_bs:";
19082        strptr_PrintBash(temp,str);
19083    }
19084    if (!(row.fix_soh == '|')) {
19085        ch_RemoveAll(temp);
19086        char_Print(row.fix_soh, temp);
19087        str << " -fix_soh:";
19088        strptr_PrintBash(temp,str);
19089    }
19090    if (!(row.fix_nl == true)) {
19091        ch_RemoveAll(temp);
19092        bool_Print(row.fix_nl, temp);
19093        str << " -fix_nl:";
19094        strptr_PrintBash(temp,str);
19095    }
19096    if (!(row.fast_msg_max == 1024)) {
19097        ch_RemoveAll(temp);
19098        i32_Print(row.fast_msg_max, temp);
19099        str << " -fast_msg_max:";
19100        strptr_PrintBash(temp,str);
19101    }
19102}
19103
19104// --- command.fast..NArgs
19105// Used with command lines
19106// Return # of command-line arguments that must follow this argument
19107// If FIELD is invalid, return -1
19108i32 command::fast_NArgs(command::FieldId field, algo::strptr& out_dflt, bool* out_anon) {
19109    i32 retval = 1;
19110    switch (field) {
19111        case command_FieldId_in: { // $comment
19112            *out_anon = false;
19113        } break;
19114        case command_FieldId_genamc: { // $comment
19115            *out_anon = false;
19116            retval=0;
19117            out_dflt="Y";
19118        } break;
19119        case command_FieldId_ns: { // bool: no argument required but value may be specified as genamc:Y
19120            *out_anon = false;
19121        } break;
19122        case command_FieldId_xml: { // bool: no argument required but value may be specified as genamc:Y
19123            *out_anon = false;
19124        } break;
19125        case command_FieldId_add_reset: { // bool: no argument required but value may be specified as genamc:Y
19126            *out_anon = false;
19127            retval=0;
19128            out_dflt="Y";
19129        } break;
19130        case command_FieldId_pretty: { // bool: no argument required but value may be specified as add_reset:Y
19131            *out_anon = false;
19132            retval=0;
19133            out_dflt="Y";
19134        } break;
19135        case command_FieldId_write: { // bool: no argument required but value may be specified as pretty:Y
19136            *out_anon = false;
19137            retval=0;
19138            out_dflt="Y";
19139        } break;
19140        case command_FieldId_encode: { // bool: no argument required but value may be specified as write:Y
19141            *out_anon = false;
19142            retval=0;
19143            out_dflt="Y";
19144        } break;
19145        case command_FieldId_decode: { // bool: no argument required but value may be specified as encode:Y
19146            *out_anon = false;
19147            retval=0;
19148            out_dflt="Y";
19149        } break;
19150        case command_FieldId_enc: { // bool: no argument required but value may be specified as decode:Y
19151            *out_anon = false;
19152        } break;
19153        case command_FieldId_nullable: { // bool: no argument required but value may be specified as decode:Y
19154            *out_anon = false;
19155            retval=0;
19156            out_dflt="Y";
19157        } break;
19158        case command_FieldId_ifmt: { // bool: no argument required but value may be specified as nullable:Y
19159            *out_anon = false;
19160        } break;
19161        case command_FieldId_ofmt: { // bool: no argument required but value may be specified as nullable:Y
19162            *out_anon = false;
19163        } break;
19164        case command_FieldId_echo: { // bool: no argument required but value may be specified as nullable:Y
19165            *out_anon = false;
19166            retval=0;
19167            out_dflt="Y";
19168        } break;
19169        case command_FieldId_hex: { // bool: no argument required but value may be specified as echo:Y
19170            *out_anon = false;
19171            retval=0;
19172            out_dflt="Y";
19173        } break;
19174        case command_FieldId_fix_bs: { // bool: no argument required but value may be specified as hex:Y
19175            *out_anon = false;
19176        } break;
19177        case command_FieldId_fix_soh: { // bool: no argument required but value may be specified as hex:Y
19178            *out_anon = false;
19179        } break;
19180        case command_FieldId_fix_nl: { // bool: no argument required but value may be specified as hex:Y
19181            *out_anon = false;
19182            retval=0;
19183            out_dflt="Y";
19184        } break;
19185        case command_FieldId_fast_msg_max: { // bool: no argument required but value may be specified as fix_nl:Y
19186            *out_anon = false;
19187        } break;
19188        default:
19189        retval=-1; // unrecognized
19190    }
19191    return retval;
19192}
19193
19194// --- command.fast_proc.fast.Start
19195// Start subprocess
19196// If subprocess already running, do nothing. Otherwise, start it
19197int command::fast_Start(command::fast_proc& parent) {
19198    int retval = 0;
19199    if (parent.pid == 0) {
19200        verblog(fast_ToCmdline(parent)); // maybe print command
19201#ifdef WIN32
19202        algo_lib::ResolveExecFname(parent.path);
19203        tempstr cmdline(fast_ToCmdline(parent));
19204        parent.pid = dospawn(Zeroterm(parent.path),Zeroterm(cmdline),parent.timeout,parent.fstdin,parent.fstdout,parent.fstderr);
19205#else
19206        parent.pid = fork();
19207        if (parent.pid == 0) { // child
19208            algo_lib::DieWithParent();
19209            if (parent.timeout > 0) {
19210                alarm(parent.timeout);
19211            }
19212            if (retval==0) retval=algo_lib::ApplyRedirect(parent.fstdin , 0);
19213            if (retval==0) retval=algo_lib::ApplyRedirect(parent.fstdout, 1);
19214            if (retval==0) retval=algo_lib::ApplyRedirect(parent.fstderr, 2);
19215            if (retval==0) retval= fast_Execv(parent);
19216            if (retval != 0) { // if start fails, print error
19217                int err=errno;
19218                prerr("command.fast_execv"
19219                <<Keyval("errno",err)
19220                <<Keyval("errstr",strerror(err))
19221                <<Keyval("comment","Execv failed"));
19222            }
19223            _exit(127); // if failed to start, exit anyway
19224        } else if (parent.pid == -1) {
19225            retval = errno; // failed to fork
19226        }
19227#endif
19228    }
19229    parent.status = parent.pid > 0 ? 0 : -1; // if didn't start, set error status
19230    return retval;
19231}
19232
19233// --- command.fast_proc.fast.StartRead
19234// Start subprocess & Read output
19235algo::Fildes command::fast_StartRead(command::fast_proc& parent, algo_lib::FFildes &read) {
19236    int pipefd[2];
19237    int rc=pipe(pipefd);
19238    (void)rc;
19239    read.fd.value = pipefd[0];
19240    parent.fstdout  << ">&" << pipefd[1];
19241    fast_Start(parent);
19242    (void)close(pipefd[1]);
19243    return read.fd;
19244}
19245
19246// --- command.fast_proc.fast.Kill
19247// Kill subprocess and wait
19248void command::fast_Kill(command::fast_proc& parent) {
19249    if (parent.pid != 0) {
19250        kill(parent.pid,9);
19251        fast_Wait(parent);
19252    }
19253}
19254
19255// --- command.fast_proc.fast.Wait
19256// Wait for subprocess to return
19257void command::fast_Wait(command::fast_proc& parent) {
19258    if (parent.pid > 0) {
19259        int wait_flags = 0;
19260        int wait_status = 0;
19261        int rc = -1;
19262        do {
19263            // really wait for subprocess to exit
19264            rc = waitpid(parent.pid,&wait_status,wait_flags);
19265        } while (rc==-1 && errno==EINTR);
19266        if (rc == parent.pid) {
19267            parent.status = wait_status;
19268            parent.pid = 0;
19269        }
19270    }
19271}
19272
19273// --- command.fast_proc.fast.Exec
19274// Start + Wait
19275// Execute subprocess and return exit code
19276int command::fast_Exec(command::fast_proc& parent) {
19277    fast_Start(parent);
19278    fast_Wait(parent);
19279    return parent.status;
19280}
19281
19282// --- command.fast_proc.fast.ExecX
19283// Start + Wait, throw exception on error
19284// Execute subprocess; throw human-readable exception on error
19285void command::fast_ExecX(command::fast_proc& parent) {
19286    int rc = fast_Exec(parent);
19287    vrfy(rc==0, tempstr() << "algo_lib.exec" << Keyval("cmd",fast_ToCmdline(parent))
19288    << Keyval("comment",algo::DescribeWaitStatus(parent.status)));
19289}
19290
19291// --- command.fast_proc.fast.Execv
19292// Call execv()
19293// Call execv with specified parameters
19294int command::fast_Execv(command::fast_proc& parent) {
19295    int ret = 0;
19296    algo::StringAry args;
19297    fast_ToArgv(parent, args);
19298    char **argv = (char**)alloca((ary_N(args)+1)*sizeof(*argv));
19299    ind_beg(algo::StringAry_ary_curs,arg,args) {
19300        argv[ind_curs(arg).index] = Zeroterm(arg);
19301    }ind_end;
19302    argv[ary_N(args)] = NULL;
19303    // if parent.path is relative, search for it in PATH
19304    algo_lib::ResolveExecFname(parent.path);
19305    ret = execv(Zeroterm(parent.path),argv);
19306    return ret;
19307}
19308
19309// --- command.fast_proc.fast.ToCmdline
19310algo::tempstr command::fast_ToCmdline(command::fast_proc& parent) {
19311    algo::tempstr retval;
19312    retval << parent.path << " ";
19313    command::fast_PrintArgv(parent.cmd,retval);
19314    if (ch_N(parent.fstdin)) {
19315        retval << " " << parent.fstdin;
19316    }
19317    if (ch_N(parent.fstdout)) {
19318        retval << " " << parent.fstdout;
19319    }
19320    if (ch_N(parent.fstderr)) {
19321        retval << " 2" << parent.fstderr;
19322    }
19323    return retval;
19324}
19325
19326// --- command.fast_proc.fast.ToArgv
19327// Form array from the command line
19328void command::fast_ToArgv(command::fast_proc& parent, algo::StringAry& args) {
19329    ary_RemoveAll(args);
19330    ary_Alloc(args) << parent.path;
19331
19332    if (parent.cmd.in != "data") {
19333        cstring *arg = &ary_Alloc(args);
19334        *arg << "-in:";
19335        cstring_Print(parent.cmd.in, *arg);
19336    }
19337
19338    if (parent.cmd.genamc != false) {
19339        cstring *arg = &ary_Alloc(args);
19340        *arg << "-genamc:";
19341        bool_Print(parent.cmd.genamc, *arg);
19342    }
19343
19344    if (parent.cmd.ns != "") {
19345        cstring *arg = &ary_Alloc(args);
19346        *arg << "-ns:";
19347        Smallstr16_Print(parent.cmd.ns, *arg);
19348    }
19349
19350    if (parent.cmd.xml != "") {
19351        cstring *arg = &ary_Alloc(args);
19352        *arg << "-xml:";
19353        cstring_Print(parent.cmd.xml, *arg);
19354    }
19355
19356    if (parent.cmd.add_reset != true) {
19357        cstring *arg = &ary_Alloc(args);
19358        *arg << "-add_reset:";
19359        bool_Print(parent.cmd.add_reset, *arg);
19360    }
19361
19362    if (parent.cmd.pretty != false) {
19363        cstring *arg = &ary_Alloc(args);
19364        *arg << "-pretty:";
19365        bool_Print(parent.cmd.pretty, *arg);
19366    }
19367
19368    if (parent.cmd.write != false) {
19369        cstring *arg = &ary_Alloc(args);
19370        *arg << "-write:";
19371        bool_Print(parent.cmd.write, *arg);
19372    }
19373
19374    if (parent.cmd.encode != false) {
19375        cstring *arg = &ary_Alloc(args);
19376        *arg << "-encode:";
19377        bool_Print(parent.cmd.encode, *arg);
19378    }
19379
19380    if (parent.cmd.decode != false) {
19381        cstring *arg = &ary_Alloc(args);
19382        *arg << "-decode:";
19383        bool_Print(parent.cmd.decode, *arg);
19384    }
19385
19386    if (parent.cmd.enc != 0) {
19387        cstring *arg = &ary_Alloc(args);
19388        *arg << "-enc:";
19389        command::enc_Print(parent.cmd, *arg);
19390    }
19391
19392    if (parent.cmd.nullable != false) {
19393        cstring *arg = &ary_Alloc(args);
19394        *arg << "-nullable:";
19395        bool_Print(parent.cmd.nullable, *arg);
19396    }
19397
19398    if (parent.cmd.ifmt != 0) {
19399        cstring *arg = &ary_Alloc(args);
19400        *arg << "-ifmt:";
19401        command::ifmt_Print(parent.cmd, *arg);
19402    }
19403
19404    if (parent.cmd.ofmt != 0) {
19405        cstring *arg = &ary_Alloc(args);
19406        *arg << "-ofmt:";
19407        command::ofmt_Print(parent.cmd, *arg);
19408    }
19409
19410    if (parent.cmd.echo != false) {
19411        cstring *arg = &ary_Alloc(args);
19412        *arg << "-echo:";
19413        bool_Print(parent.cmd.echo, *arg);
19414    }
19415
19416    if (parent.cmd.hex != false) {
19417        cstring *arg = &ary_Alloc(args);
19418        *arg << "-hex:";
19419        bool_Print(parent.cmd.hex, *arg);
19420    }
19421
19422    if (parent.cmd.fix_bs != "FIX.4.2") {
19423        cstring *arg = &ary_Alloc(args);
19424        *arg << "-fix_bs:";
19425        Smallstr10_Print(parent.cmd.fix_bs, *arg);
19426    }
19427
19428    if (parent.cmd.fix_soh != '|') {
19429        cstring *arg = &ary_Alloc(args);
19430        *arg << "-fix_soh:";
19431        char_Print(parent.cmd.fix_soh, *arg);
19432    }
19433
19434    if (parent.cmd.fix_nl != true) {
19435        cstring *arg = &ary_Alloc(args);
19436        *arg << "-fix_nl:";
19437        bool_Print(parent.cmd.fix_nl, *arg);
19438    }
19439
19440    if (parent.cmd.fast_msg_max != 1024) {
19441        cstring *arg = &ary_Alloc(args);
19442        *arg << "-fast_msg_max:";
19443        i32_Print(parent.cmd.fast_msg_max, *arg);
19444    }
19445    for (int i=1; i < algo_lib::_db.cmdline.verbose; ++i) {
19446        ary_Alloc(args) << "-verbose";
19447    }
19448}
19449
19450// --- command.fast_proc..Uninit
19451void command::fast_proc_Uninit(command::fast_proc& parent) {
19452    command::fast_proc &row = parent; (void)row;
19453
19454    // command.fast_proc.fast.Uninit (Exec)  //
19455    fast_Kill(parent); // kill child, ensure forward progress
19456}
19457
19458// --- command.gcache.cmd.Addary
19459// Reserve space (this may move memory). Insert N element at the end.
19460// Return aryptr to newly inserted block.
19461// If the RHS argument aliases the array (refers to the same memory), exit program with fatal error.
19462algo::aryptr<algo::cstring> command::cmd_Addary(command::gcache& parent, algo::aryptr<algo::cstring> rhs) {
19463    bool overlaps = rhs.n_elems>0 && rhs.elems >= parent.cmd_elems && rhs.elems < parent.cmd_elems + parent.cmd_max;
19464    if (UNLIKELY(overlaps)) {
19465        FatalErrorExit("command.tary_alias  field:command.gcache.cmd  comment:'alias error: sub-array is being appended to the whole'");
19466    }
19467    int nnew = rhs.n_elems;
19468    cmd_Reserve(parent, nnew); // reserve space
19469    int at = parent.cmd_n;
19470    for (int i = 0; i < nnew; i++) {
19471        new (parent.cmd_elems + at + i) algo::cstring(rhs[i]);
19472        parent.cmd_n++;
19473    }
19474    return algo::aryptr<algo::cstring>(parent.cmd_elems + at, nnew);
19475}
19476
19477// --- command.gcache.cmd.Alloc
19478// Reserve space. Insert element at the end
19479// The new element is initialized to a default value
19480algo::cstring& command::cmd_Alloc(command::gcache& parent) {
19481    cmd_Reserve(parent, 1);
19482    int n  = parent.cmd_n;
19483    int at = n;
19484    algo::cstring *elems = parent.cmd_elems;
19485    new (elems + at) algo::cstring(); // construct new element, default initializer
19486    parent.cmd_n = n+1;
19487    return elems[at];
19488}
19489
19490// --- command.gcache.cmd.AllocAt
19491// Reserve space for new element, reallocating the array if necessary
19492// Insert new element at specified index. Index must be in range or a fatal error occurs.
19493algo::cstring& command::cmd_AllocAt(command::gcache& parent, int at) {
19494    cmd_Reserve(parent, 1);
19495    int n  = parent.cmd_n;
19496    if (UNLIKELY(u64(at) >= u64(n+1))) {
19497        FatalErrorExit("command.bad_alloc_at  field:command.gcache.cmd  comment:'index out of range'");
19498    }
19499    algo::cstring *elems = parent.cmd_elems;
19500    memmove(elems + at + 1, elems + at, (n - at) * sizeof(algo::cstring));
19501    new (elems + at) algo::cstring(); // construct element, default initializer
19502    parent.cmd_n = n+1;
19503    return elems[at];
19504}
19505
19506// --- command.gcache.cmd.AllocN
19507// Reserve space. Insert N elements at the end of the array, return pointer to array
19508algo::aryptr<algo::cstring> command::cmd_AllocN(command::gcache& parent, int n_elems) {
19509    cmd_Reserve(parent, n_elems);
19510    int old_n  = parent.cmd_n;
19511    int new_n = old_n + n_elems;
19512    algo::cstring *elems = parent.cmd_elems;
19513    for (int i = old_n; i < new_n; i++) {
19514        new (elems + i) algo::cstring(); // construct new element, default initialize
19515    }
19516    parent.cmd_n = new_n;
19517    return algo::aryptr<algo::cstring>(elems + old_n, n_elems);
19518}
19519
19520// --- command.gcache.cmd.Remove
19521// Remove item by index. If index outside of range, do nothing.
19522void command::cmd_Remove(command::gcache& parent, u32 i) {
19523    u32 lim = parent.cmd_n;
19524    algo::cstring *elems = parent.cmd_elems;
19525    if (i < lim) {
19526        elems[i].~cstring(); // destroy element
19527        memmove(elems + i, elems + (i + 1), sizeof(algo::cstring) * (lim - (i + 1)));
19528        parent.cmd_n = lim - 1;
19529    }
19530}
19531
19532// --- command.gcache.cmd.RemoveAll
19533void command::cmd_RemoveAll(command::gcache& parent) {
19534    u32 n = parent.cmd_n;
19535    while (n > 0) {
19536        n -= 1;
19537        parent.cmd_elems[n].~cstring();
19538        parent.cmd_n = n;
19539    }
19540}
19541
19542// --- command.gcache.cmd.RemoveLast
19543// Delete last element of array. Do nothing if array is empty.
19544void command::cmd_RemoveLast(command::gcache& parent) {
19545    u64 n = parent.cmd_n;
19546    if (n > 0) {
19547        n -= 1;
19548        cmd_qFind(parent, u64(n)).~cstring();
19549        parent.cmd_n = n;
19550    }
19551}
19552
19553// --- command.gcache.cmd.AbsReserve
19554// Make sure N elements fit in array. Process dies if out of memory
19555void command::cmd_AbsReserve(command::gcache& parent, int n) {
19556    u32 old_max  = parent.cmd_max;
19557    if (n > i32(old_max)) {
19558        u32 new_max  = i32_Max(i32_Max(old_max * 2, n), 4);
19559        void *new_mem = algo_lib::malloc_ReallocMem(parent.cmd_elems, old_max * sizeof(algo::cstring), new_max * sizeof(algo::cstring));
19560        if (UNLIKELY(!new_mem)) {
19561            FatalErrorExit("command.tary_nomem  field:command.gcache.cmd  comment:'out of memory'");
19562        }
19563        parent.cmd_elems = (algo::cstring*)new_mem;
19564        parent.cmd_max = new_max;
19565    }
19566}
19567
19568// --- command.gcache.cmd.Setary
19569// Copy contents of RHS to PARENT.
19570void command::cmd_Setary(command::gcache& parent, command::gcache &rhs) {
19571    cmd_RemoveAll(parent);
19572    int nnew = rhs.cmd_n;
19573    cmd_Reserve(parent, nnew); // reserve space
19574    for (int i = 0; i < nnew; i++) { // copy elements over
19575        new (parent.cmd_elems + i) algo::cstring(cmd_qFind(rhs, i));
19576        parent.cmd_n = i + 1;
19577    }
19578}
19579
19580// --- command.gcache.cmd.Setary2
19581// Copy specified array into cmd, discarding previous contents.
19582// If the RHS argument aliases the array (refers to the same memory), throw exception.
19583void command::cmd_Setary(command::gcache& parent, const algo::aryptr<algo::cstring> &rhs) {
19584    cmd_RemoveAll(parent);
19585    cmd_Addary(parent, rhs);
19586}
19587
19588// --- command.gcache.cmd.AllocNVal
19589// Reserve space. Insert N elements at the end of the array, return pointer to array
19590algo::aryptr<algo::cstring> command::cmd_AllocNVal(command::gcache& parent, int n_elems, const algo::cstring& val) {
19591    cmd_Reserve(parent, n_elems);
19592    int old_n  = parent.cmd_n;
19593    int new_n = old_n + n_elems;
19594    algo::cstring *elems = parent.cmd_elems;
19595    for (int i = old_n; i < new_n; i++) {
19596        new (elems + i) algo::cstring(val);
19597    }
19598    parent.cmd_n = new_n;
19599    return algo::aryptr<algo::cstring>(elems + old_n, n_elems);
19600}
19601
19602// --- command.gcache.cmd.ReadStrptrMaybe
19603// A single element is read from input string and appended to the array.
19604// If the string contains an error, the array is untouched.
19605// Function returns success value.
19606bool command::cmd_ReadStrptrMaybe(command::gcache& parent, algo::strptr in_str) {
19607    bool retval = true;
19608    algo::cstring &elem = cmd_Alloc(parent);
19609    retval = algo::cstring_ReadStrptrMaybe(elem, in_str);
19610    if (!retval) {
19611        cmd_RemoveLast(parent);
19612    }
19613    return retval;
19614}
19615
19616// --- command.gcache..ReadFieldMaybe
19617bool command::gcache_ReadFieldMaybe(command::gcache& parent, algo::strptr field, algo::strptr strval) {
19618    bool retval = true;
19619    command::FieldId field_id;
19620    (void)value_SetStrptrMaybe(field_id,algo::Pathcomp(field, ".LL"));
19621    switch(field_id) {
19622        case command_FieldId_in: {
19623            retval = algo::cstring_ReadStrptrMaybe(parent.in, strval);
19624            break;
19625        }
19626        case command_FieldId_cmd: {
19627            retval = cmd_ReadStrptrMaybe(parent, strval);
19628            break;
19629        }
19630        case command_FieldId_install: {
19631            retval = bool_ReadStrptrMaybe(parent.install, strval);
19632            break;
19633        }
19634        case command_FieldId_stats: {
19635            retval = bool_ReadStrptrMaybe(parent.stats, strval);
19636            break;
19637        }
19638        case command_FieldId_enable: {
19639            retval = bool_ReadStrptrMaybe(parent.enable, strval);
19640            break;
19641        }
19642        case command_FieldId_disable: {
19643            retval = bool_ReadStrptrMaybe(parent.disable, strval);
19644            break;
19645        }
19646        case command_FieldId_gc: {
19647            retval = bool_ReadStrptrMaybe(parent.gc, strval);
19648            break;
19649        }
19650        case command_FieldId_clean: {
19651            retval = bool_ReadStrptrMaybe(parent.clean, strval);
19652            break;
19653        }
19654        case command_FieldId_dir: {
19655            retval = algo::cstring_ReadStrptrMaybe(parent.dir, strval);
19656            break;
19657        }
19658        case command_FieldId_hitrate: {
19659            retval = bool_ReadStrptrMaybe(parent.hitrate, strval);
19660            break;
19661        }
19662        case command_FieldId_after: {
19663            retval = algo::UnTime_ReadStrptrMaybe(parent.after, strval);
19664            break;
19665        }
19666        case command_FieldId_report: {
19667            retval = bool_ReadStrptrMaybe(parent.report, strval);
19668            break;
19669        }
19670        case command_FieldId_force: {
19671            retval = bool_ReadStrptrMaybe(parent.force, strval);
19672            break;
19673        }
19674        default: break;
19675    }
19676    if (!retval) {
19677        algo_lib::AppendErrtext("attr",field);
19678    }
19679    return retval;
19680}
19681
19682// --- command.gcache..ReadTupleMaybe
19683// Read fields of command::gcache from attributes of ascii tuple TUPLE
19684bool command::gcache_ReadTupleMaybe(command::gcache &parent, algo::Tuple &tuple) {
19685    bool retval = true;
19686    int anon_idx = 0;
19687    ind_beg(algo::Tuple_attrs_curs,attr,tuple) {
19688        if (ch_N(attr.name) == 0) {
19689            attr.name = gcache_GetAnon(parent, anon_idx++);
19690        }
19691        retval = gcache_ReadFieldMaybe(parent, attr.name, attr.value);
19692        if (!retval) {
19693            break;
19694        }
19695    }ind_end;
19696    return retval;
19697}
19698
19699// --- command.gcache..Init
19700// Set all fields to initial values.
19701void command::gcache_Init(command::gcache& parent) {
19702    parent.in = algo::strptr("data");
19703    parent.cmd_elems 	= 0; // (command.gcache.cmd)
19704    parent.cmd_n     	= 0; // (command.gcache.cmd)
19705    parent.cmd_max   	= 0; // (command.gcache.cmd)
19706    parent.install = bool(false);
19707    parent.stats = bool(false);
19708    parent.enable = bool(false);
19709    parent.disable = bool(false);
19710    parent.gc = bool(false);
19711    parent.clean = bool(false);
19712    parent.dir = algo::strptr("/tmp/gcache");
19713    parent.hitrate = bool(false);
19714    parent.report = bool(false);
19715    parent.force = bool(false);
19716}
19717
19718// --- command.gcache..Uninit
19719void command::gcache_Uninit(command::gcache& parent) {
19720    command::gcache &row = parent; (void)row;
19721
19722    // command.gcache.cmd.Uninit (Tary)  //Command to execute
19723    // remove all elements from command.gcache.cmd
19724    cmd_RemoveAll(parent);
19725    // free memory for Tary command.gcache.cmd
19726    algo_lib::malloc_FreeMem(parent.cmd_elems, sizeof(algo::cstring)*parent.cmd_max); // (command.gcache.cmd)
19727}
19728
19729// --- command.gcache..ToCmdline
19730// Convenience function that returns a full command line
19731// Assume command is in a directory called bin
19732tempstr command::gcache_ToCmdline(command::gcache& row) {
19733    tempstr ret;
19734    ret << "bin/gcache ";
19735    gcache_PrintArgv(row, ret);
19736    // inherit less intense verbose, debug options
19737    for (int i = 1; i < algo_lib::_db.cmdline.verbose; i++) {
19738        ret << " -verbose";
19739    }
19740    for (int i = 1; i < algo_lib::_db.cmdline.debug; i++) {
19741        ret << " -debug";
19742    }
19743    return ret;
19744}
19745
19746// --- command.gcache..PrintArgv
19747// print string representation of ROW to string STR
19748// cfmt:command.gcache.Argv  printfmt:Tuple
19749void command::gcache_PrintArgv(command::gcache& row, algo::cstring& str) {
19750    algo::tempstr temp;
19751    (void)temp;
19752    (void)str;
19753    if (!(row.in == "data")) {
19754        ch_RemoveAll(temp);
19755        cstring_Print(row.in, temp);
19756        str << " -in:";
19757        strptr_PrintBash(temp,str);
19758    }
19759    ind_beg(gcache_cmd_curs,value,row) {
19760        ch_RemoveAll(temp);
19761        cstring_Print(value, temp);
19762        str << " -cmd:";
19763        strptr_PrintBash(temp,str);
19764    }ind_end;
19765    if (!(row.install == false)) {
19766        ch_RemoveAll(temp);
19767        bool_Print(row.install, temp);
19768        str << " -install:";
19769        strptr_PrintBash(temp,str);
19770    }
19771    if (!(row.stats == false)) {
19772        ch_RemoveAll(temp);
19773        bool_Print(row.stats, temp);
19774        str << " -stats:";
19775        strptr_PrintBash(temp,str);
19776    }
19777    if (!(row.enable == false)) {
19778        ch_RemoveAll(temp);
19779        bool_Print(row.enable, temp);
19780        str << " -enable:";
19781        strptr_PrintBash(temp,str);
19782    }
19783    if (!(row.disable == false)) {
19784        ch_RemoveAll(temp);
19785        bool_Print(row.disable, temp);
19786        str << " -disable:";
19787        strptr_PrintBash(temp,str);
19788    }
19789    if (!(row.gc == false)) {
19790        ch_RemoveAll(temp);
19791        bool_Print(row.gc, temp);
19792        str << " -gc:";
19793        strptr_PrintBash(temp,str);
19794    }
19795    if (!(row.clean == false)) {
19796        ch_RemoveAll(temp);
19797        bool_Print(row.clean, temp);
19798        str << " -clean:";
19799        strptr_PrintBash(temp,str);
19800    }
19801    if (!(row.dir == "/tmp/gcache")) {
19802        ch_RemoveAll(temp);
19803        cstring_Print(row.dir, temp);
19804        str << " -dir:";
19805        strptr_PrintBash(temp,str);
19806    }
19807    if (!(row.hitrate == false)) {
19808        ch_RemoveAll(temp);
19809        bool_Print(row.hitrate, temp);
19810        str << " -hitrate:";
19811        strptr_PrintBash(temp,str);
19812    }
19813    if (!(UnTime_Eq(row.after, algo::UnTime()))) {
19814        ch_RemoveAll(temp);
19815        UnTime_Print(row.after, temp);
19816        str << " -after:";
19817        strptr_PrintBash(temp,str);
19818    }
19819    if (!(row.report == false)) {
19820        ch_RemoveAll(temp);
19821        bool_Print(row.report, temp);
19822        str << " -report:";
19823        strptr_PrintBash(temp,str);
19824    }
19825    if (!(row.force == false)) {
19826        ch_RemoveAll(temp);
19827        bool_Print(row.force, temp);
19828        str << " -force:";
19829        strptr_PrintBash(temp,str);
19830    }
19831}
19832
19833// --- command.gcache..Print
19834// print string representation of ROW to string STR
19835// cfmt:command.gcache.String  printfmt:Tuple
19836void command::gcache_Print(command::gcache& row, algo::cstring& str) {
19837    algo::tempstr temp;
19838    str << "command.gcache";
19839
19840    algo::cstring_Print(row.in, temp);
19841    PrintAttrSpaceReset(str,"in", temp);
19842
19843    ind_beg(gcache_cmd_curs,cmd,row) {
19844        algo::cstring_Print(cmd, temp);
19845        tempstr name;
19846        name << "cmd.";
19847        name << ind_curs(cmd).index;
19848        PrintAttrSpaceReset(str, name, temp);
19849    }ind_end;
19850
19851    bool_Print(row.install, temp);
19852    PrintAttrSpaceReset(str,"install", temp);
19853
19854    bool_Print(row.stats, temp);
19855    PrintAttrSpaceReset(str,"stats", temp);
19856
19857    bool_Print(row.enable, temp);
19858    PrintAttrSpaceReset(str,"enable", temp);
19859
19860    bool_Print(row.disable, temp);
19861    PrintAttrSpaceReset(str,"disable", temp);
19862
19863    bool_Print(row.gc, temp);
19864    PrintAttrSpaceReset(str,"gc", temp);
19865
19866    bool_Print(row.clean, temp);
19867    PrintAttrSpaceReset(str,"clean", temp);
19868
19869    algo::cstring_Print(row.dir, temp);
19870    PrintAttrSpaceReset(str,"dir", temp);
19871
19872    bool_Print(row.hitrate, temp);
19873    PrintAttrSpaceReset(str,"hitrate", temp);
19874
19875    algo::UnTime_Print(row.after, temp);
19876    PrintAttrSpaceReset(str,"after", temp);
19877
19878    bool_Print(row.report, temp);
19879    PrintAttrSpaceReset(str,"report", temp);
19880
19881    bool_Print(row.force, temp);
19882    PrintAttrSpaceReset(str,"force", temp);
19883}
19884
19885// --- command.gcache..GetAnon
19886algo::strptr command::gcache_GetAnon(command::gcache &parent, i32 idx) {
19887    (void)parent;//only to avoid -Wunused-parameter
19888    switch(idx) {
19889        default: return strptr("cmd", 3);
19890    }
19891}
19892
19893// --- command.gcache..NArgs
19894// Used with command lines
19895// Return # of command-line arguments that must follow this argument
19896// If FIELD is invalid, return -1
19897i32 command::gcache_NArgs(command::FieldId field, algo::strptr& out_dflt, bool* out_anon) {
19898    i32 retval = 1;
19899    switch (field) {
19900        case command_FieldId_in: { // $comment
19901            *out_anon = false;
19902        } break;
19903        case command_FieldId_cmd: { // $comment
19904            *out_anon = true;
19905        } break;
19906        case command_FieldId_install: { // $comment
19907            *out_anon = false;
19908            retval=0;
19909            out_dflt="Y";
19910        } break;
19911        case command_FieldId_stats: { // bool: no argument required but value may be specified as install:Y
19912            *out_anon = false;
19913            retval=0;
19914            out_dflt="Y";
19915        } break;
19916        case command_FieldId_enable: { // bool: no argument required but value may be specified as stats:Y
19917            *out_anon = false;
19918            retval=0;
19919            out_dflt="Y";
19920        } break;
19921        case command_FieldId_disable: { // bool: no argument required but value may be specified as enable:Y
19922            *out_anon = false;
19923            retval=0;
19924            out_dflt="Y";
19925        } break;
19926        case command_FieldId_gc: { // bool: no argument required but value may be specified as disable:Y
19927            *out_anon = false;
19928            retval=0;
19929            out_dflt="Y";
19930        } break;
19931        case command_FieldId_clean: { // bool: no argument required but value may be specified as gc:Y
19932            *out_anon = false;
19933            retval=0;
19934            out_dflt="Y";
19935        } break;
19936        case command_FieldId_dir: { // bool: no argument required but value may be specified as clean:Y
19937            *out_anon = false;
19938        } break;
19939        case command_FieldId_hitrate: { // bool: no argument required but value may be specified as clean:Y
19940            *out_anon = false;
19941            retval=0;
19942            out_dflt="Y";
19943        } break;
19944        case command_FieldId_after: { // bool: no argument required but value may be specified as hitrate:Y
19945            *out_anon = false;
19946        } break;
19947        case command_FieldId_report: { // bool: no argument required but value may be specified as hitrate:Y
19948            *out_anon = false;
19949            retval=0;
19950            out_dflt="Y";
19951        } break;
19952        case command_FieldId_force: { // bool: no argument required but value may be specified as report:Y
19953            *out_anon = false;
19954            retval=0;
19955            out_dflt="Y";
19956        } break;
19957        default:
19958        retval=-1; // unrecognized
19959    }
19960    return retval;
19961}
19962
19963// --- command.gcache_proc.gcache.Start
19964// Start subprocess
19965// If subprocess already running, do nothing. Otherwise, start it
19966int command::gcache_Start(command::gcache_proc& parent) {
19967    int retval = 0;
19968    if (parent.pid == 0) {
19969        verblog(gcache_ToCmdline(parent)); // maybe print command
19970#ifdef WIN32
19971        algo_lib::ResolveExecFname(parent.path);
19972        tempstr cmdline(gcache_ToCmdline(parent));
19973        parent.pid = dospawn(Zeroterm(parent.path),Zeroterm(cmdline),parent.timeout,parent.fstdin,parent.fstdout,parent.fstderr);
19974#else
19975        parent.pid = fork();
19976        if (parent.pid == 0) { // child
19977            algo_lib::DieWithParent();
19978            if (parent.timeout > 0) {
19979                alarm(parent.timeout);
19980            }
19981            if (retval==0) retval=algo_lib::ApplyRedirect(parent.fstdin , 0);
19982            if (retval==0) retval=algo_lib::ApplyRedirect(parent.fstdout, 1);
19983            if (retval==0) retval=algo_lib::ApplyRedirect(parent.fstderr, 2);
19984            if (retval==0) retval= gcache_Execv(parent);
19985            if (retval != 0) { // if start fails, print error
19986                int err=errno;
19987                prerr("command.gcache_execv"
19988                <<Keyval("errno",err)
19989                <<Keyval("errstr",strerror(err))
19990                <<Keyval("comment","Execv failed"));
19991            }
19992            _exit(127); // if failed to start, exit anyway
19993        } else if (parent.pid == -1) {
19994            retval = errno; // failed to fork
19995        }
19996#endif
19997    }
19998    parent.status = parent.pid > 0 ? 0 : -1; // if didn't start, set error status
19999    return retval;
20000}
20001
20002// --- command.gcache_proc.gcache.StartRead
20003// Start subprocess & Read output
20004algo::Fildes command::gcache_StartRead(command::gcache_proc& parent, algo_lib::FFildes &read) {
20005    int pipefd[2];
20006    int rc=pipe(pipefd);
20007    (void)rc;
20008    read.fd.value = pipefd[0];
20009    parent.fstdout  << ">&" << pipefd[1];
20010    gcache_Start(parent);
20011    (void)close(pipefd[1]);
20012    return read.fd;
20013}
20014
20015// --- command.gcache_proc.gcache.Kill
20016// Kill subprocess and wait
20017void command::gcache_Kill(command::gcache_proc& parent) {
20018    if (parent.pid != 0) {
20019        kill(parent.pid,9);
20020        gcache_Wait(parent);
20021    }
20022}
20023
20024// --- command.gcache_proc.gcache.Wait
20025// Wait for subprocess to return
20026void command::gcache_Wait(command::gcache_proc& parent) {
20027    if (parent.pid > 0) {
20028        int wait_flags = 0;
20029        int wait_status = 0;
20030        int rc = -1;
20031        do {
20032            // really wait for subprocess to exit
20033            rc = waitpid(parent.pid,&wait_status,wait_flags);
20034        } while (rc==-1 && errno==EINTR);
20035        if (rc == parent.pid) {
20036            parent.status = wait_status;
20037            parent.pid = 0;
20038        }
20039    }
20040}
20041
20042// --- command.gcache_proc.gcache.Exec
20043// Start + Wait
20044// Execute subprocess and return exit code
20045int command::gcache_Exec(command::gcache_proc& parent) {
20046    gcache_Start(parent);
20047    gcache_Wait(parent);
20048    return parent.status;
20049}
20050
20051// --- command.gcache_proc.gcache.ExecX
20052// Start + Wait, throw exception on error
20053// Execute subprocess; throw human-readable exception on error
20054void command::gcache_ExecX(command::gcache_proc& parent) {
20055    int rc = gcache_Exec(parent);
20056    vrfy(rc==0, tempstr() << "algo_lib.exec" << Keyval("cmd",gcache_ToCmdline(parent))
20057    << Keyval("comment",algo::DescribeWaitStatus(parent.status)));
20058}
20059
20060// --- command.gcache_proc.gcache.Execv
20061// Call execv()
20062// Call execv with specified parameters
20063int command::gcache_Execv(command::gcache_proc& parent) {
20064    int ret = 0;
20065    algo::StringAry args;
20066    gcache_ToArgv(parent, args);
20067    char **argv = (char**)alloca((ary_N(args)+1)*sizeof(*argv));
20068    ind_beg(algo::StringAry_ary_curs,arg,args) {
20069        argv[ind_curs(arg).index] = Zeroterm(arg);
20070    }ind_end;
20071    argv[ary_N(args)] = NULL;
20072    // if parent.path is relative, search for it in PATH
20073    algo_lib::ResolveExecFname(parent.path);
20074    ret = execv(Zeroterm(parent.path),argv);
20075    return ret;
20076}
20077
20078// --- command.gcache_proc.gcache.ToCmdline
20079algo::tempstr command::gcache_ToCmdline(command::gcache_proc& parent) {
20080    algo::tempstr retval;
20081    retval << parent.path << " ";
20082    command::gcache_PrintArgv(parent.cmd,retval);
20083    if (ch_N(parent.fstdin)) {
20084        retval << " " << parent.fstdin;
20085    }
20086    if (ch_N(parent.fstdout)) {
20087        retval << " " << parent.fstdout;
20088    }
20089    if (ch_N(parent.fstderr)) {
20090        retval << " 2" << parent.fstderr;
20091    }
20092    return retval;
20093}
20094
20095// --- command.gcache_proc.gcache.ToArgv
20096// Form array from the command line
20097void command::gcache_ToArgv(command::gcache_proc& parent, algo::StringAry& args) {
20098    ary_RemoveAll(args);
20099    ary_Alloc(args) << parent.path;
20100
20101    if (parent.cmd.in != "data") {
20102        cstring *arg = &ary_Alloc(args);
20103        *arg << "-in:";
20104        cstring_Print(parent.cmd.in, *arg);
20105    }
20106    ind_beg(command::gcache_cmd_curs,value,parent.cmd) {
20107        cstring *arg = &ary_Alloc(args);
20108        *arg << "-cmd:";
20109        cstring_Print(value, *arg);
20110    }ind_end;
20111
20112    if (parent.cmd.install != false) {
20113        cstring *arg = &ary_Alloc(args);
20114        *arg << "-install:";
20115        bool_Print(parent.cmd.install, *arg);
20116    }
20117
20118    if (parent.cmd.stats != false) {
20119        cstring *arg = &ary_Alloc(args);
20120        *arg << "-stats:";
20121        bool_Print(parent.cmd.stats, *arg);
20122    }
20123
20124    if (parent.cmd.enable != false) {
20125        cstring *arg = &ary_Alloc(args);
20126        *arg << "-enable:";
20127        bool_Print(parent.cmd.enable, *arg);
20128    }
20129
20130    if (parent.cmd.disable != false) {
20131        cstring *arg = &ary_Alloc(args);
20132        *arg << "-disable:";
20133        bool_Print(parent.cmd.disable, *arg);
20134    }
20135
20136    if (parent.cmd.gc != false) {
20137        cstring *arg = &ary_Alloc(args);
20138        *arg << "-gc:";
20139        bool_Print(parent.cmd.gc, *arg);
20140    }
20141
20142    if (parent.cmd.clean != false) {
20143        cstring *arg = &ary_Alloc(args);
20144        *arg << "-clean:";
20145        bool_Print(parent.cmd.clean, *arg);
20146    }
20147
20148    if (parent.cmd.dir != "/tmp/gcache") {
20149        cstring *arg = &ary_Alloc(args);
20150        *arg << "-dir:";
20151        cstring_Print(parent.cmd.dir, *arg);
20152    }
20153
20154    if (parent.cmd.hitrate != false) {
20155        cstring *arg = &ary_Alloc(args);
20156        *arg << "-hitrate:";
20157        bool_Print(parent.cmd.hitrate, *arg);
20158    }
20159
20160    if (true) {
20161        cstring *arg = &ary_Alloc(args);
20162        *arg << "-after:";
20163        UnTime_Print(parent.cmd.after, *arg);
20164    }
20165
20166    if (parent.cmd.report != false) {
20167        cstring *arg = &ary_Alloc(args);
20168        *arg << "-report:";
20169        bool_Print(parent.cmd.report, *arg);
20170    }
20171
20172    if (parent.cmd.force != false) {
20173        cstring *arg = &ary_Alloc(args);
20174        *arg << "-force:";
20175        bool_Print(parent.cmd.force, *arg);
20176    }
20177    for (int i=1; i < algo_lib::_db.cmdline.verbose; ++i) {
20178        ary_Alloc(args) << "-verbose";
20179    }
20180}
20181
20182// --- command.gcache_proc..Uninit
20183void command::gcache_proc_Uninit(command::gcache_proc& parent) {
20184    command::gcache_proc &row = parent; (void)row;
20185
20186    // command.gcache_proc.gcache.Uninit (Exec)  //
20187    gcache_Kill(parent); // kill child, ensure forward progress
20188}
20189
20190// --- command.gcli.fields.Addary
20191// Reserve space (this may move memory). Insert N element at the end.
20192// Return aryptr to newly inserted block.
20193// If the RHS argument aliases the array (refers to the same memory), exit program with fatal error.
20194algo::aryptr<algo::cstring> command::fields_Addary(command::gcli& parent, algo::aryptr<algo::cstring> rhs) {
20195    bool overlaps = rhs.n_elems>0 && rhs.elems >= parent.fields_elems && rhs.elems < parent.fields_elems + parent.fields_max;
20196    if (UNLIKELY(overlaps)) {
20197        FatalErrorExit("command.tary_alias  field:command.gcli.fields  comment:'alias error: sub-array is being appended to the whole'");
20198    }
20199    int nnew = rhs.n_elems;
20200    fields_Reserve(parent, nnew); // reserve space
20201    int at = parent.fields_n;
20202    for (int i = 0; i < nnew; i++) {
20203        new (parent.fields_elems + at + i) algo::cstring(rhs[i]);
20204        parent.fields_n++;
20205    }
20206    return algo::aryptr<algo::cstring>(parent.fields_elems + at, nnew);
20207}
20208
20209// --- command.gcli.fields.Alloc
20210// Reserve space. Insert element at the end
20211// The new element is initialized to a default value
20212algo::cstring& command::fields_Alloc(command::gcli& parent) {
20213    fields_Reserve(parent, 1);
20214    int n  = parent.fields_n;
20215    int at = n;
20216    algo::cstring *elems = parent.fields_elems;
20217    new (elems + at) algo::cstring(""); // construct new element, default initializer
20218    parent.fields_n = n+1;
20219    return elems[at];
20220}
20221
20222// --- command.gcli.fields.AllocAt
20223// Reserve space for new element, reallocating the array if necessary
20224// Insert new element at specified index. Index must be in range or a fatal error occurs.
20225algo::cstring& command::fields_AllocAt(command::gcli& parent, int at) {
20226    fields_Reserve(parent, 1);
20227    int n  = parent.fields_n;
20228    if (UNLIKELY(u64(at) >= u64(n+1))) {
20229        FatalErrorExit("command.bad_alloc_at  field:command.gcli.fields  comment:'index out of range'");
20230    }
20231    algo::cstring *elems = parent.fields_elems;
20232    memmove(elems + at + 1, elems + at, (n - at) * sizeof(algo::cstring));
20233    new (elems + at) algo::cstring(""); // construct element, default initializer
20234    parent.fields_n = n+1;
20235    return elems[at];
20236}
20237
20238// --- command.gcli.fields.AllocN
20239// Reserve space. Insert N elements at the end of the array, return pointer to array
20240algo::aryptr<algo::cstring> command::fields_AllocN(command::gcli& parent, int n_elems) {
20241    fields_Reserve(parent, n_elems);
20242    int old_n  = parent.fields_n;
20243    int new_n = old_n + n_elems;
20244    algo::cstring *elems = parent.fields_elems;
20245    for (int i = old_n; i < new_n; i++) {
20246        new (elems + i) algo::cstring(""); // construct new element, default initialize
20247    }
20248    parent.fields_n = new_n;
20249    return algo::aryptr<algo::cstring>(elems + old_n, n_elems);
20250}
20251
20252// --- command.gcli.fields.Remove
20253// Remove item by index. If index outside of range, do nothing.
20254void command::fields_Remove(command::gcli& parent, u32 i) {
20255    u32 lim = parent.fields_n;
20256    algo::cstring *elems = parent.fields_elems;
20257    if (i < lim) {
20258        elems[i].~cstring(); // destroy element
20259        memmove(elems + i, elems + (i + 1), sizeof(algo::cstring) * (lim - (i + 1)));
20260        parent.fields_n = lim - 1;
20261    }
20262}
20263
20264// --- command.gcli.fields.RemoveAll
20265void command::fields_RemoveAll(command::gcli& parent) {
20266    u32 n = parent.fields_n;
20267    while (n > 0) {
20268        n -= 1;
20269        parent.fields_elems[n].~cstring();
20270        parent.fields_n = n;
20271    }
20272}
20273
20274// --- command.gcli.fields.RemoveLast
20275// Delete last element of array. Do nothing if array is empty.
20276void command::fields_RemoveLast(command::gcli& parent) {
20277    u64 n = parent.fields_n;
20278    if (n > 0) {
20279        n -= 1;
20280        fields_qFind(parent, u64(n)).~cstring();
20281        parent.fields_n = n;
20282    }
20283}
20284
20285// --- command.gcli.fields.AbsReserve
20286// Make sure N elements fit in array. Process dies if out of memory
20287void command::fields_AbsReserve(command::gcli& parent, int n) {
20288    u32 old_max  = parent.fields_max;
20289    if (n > i32(old_max)) {
20290        u32 new_max  = i32_Max(i32_Max(old_max * 2, n), 4);
20291        void *new_mem = algo_lib::malloc_ReallocMem(parent.fields_elems, old_max * sizeof(algo::cstring), new_max * sizeof(algo::cstring));
20292        if (UNLIKELY(!new_mem)) {
20293            FatalErrorExit("command.tary_nomem  field:command.gcli.fields  comment:'out of memory'");
20294        }
20295        parent.fields_elems = (algo::cstring*)new_mem;
20296        parent.fields_max = new_max;
20297    }
20298}
20299
20300// --- command.gcli.fields.Setary
20301// Copy contents of RHS to PARENT.
20302void command::fields_Setary(command::gcli& parent, command::gcli &rhs) {
20303    fields_RemoveAll(parent);
20304    int nnew = rhs.fields_n;
20305    fields_Reserve(parent, nnew); // reserve space
20306    for (int i = 0; i < nnew; i++) { // copy elements over
20307        new (parent.fields_elems + i) algo::cstring(fields_qFind(rhs, i));
20308        parent.fields_n = i + 1;
20309    }
20310}
20311
20312// --- command.gcli.fields.Setary2
20313// Copy specified array into fields, discarding previous contents.
20314// If the RHS argument aliases the array (refers to the same memory), throw exception.
20315void command::fields_Setary(command::gcli& parent, const algo::aryptr<algo::cstring> &rhs) {
20316    fields_RemoveAll(parent);
20317    fields_Addary(parent, rhs);
20318}
20319
20320// --- command.gcli.fields.AllocNVal
20321// Reserve space. Insert N elements at the end of the array, return pointer to array
20322algo::aryptr<algo::cstring> command::fields_AllocNVal(command::gcli& parent, int n_elems, const algo::cstring& val) {
20323    fields_Reserve(parent, n_elems);
20324    int old_n  = parent.fields_n;
20325    int new_n = old_n + n_elems;
20326    algo::cstring *elems = parent.fields_elems;
20327    for (int i = old_n; i < new_n; i++) {
20328        new (elems + i) algo::cstring(val);
20329    }
20330    parent.fields_n = new_n;
20331    return algo::aryptr<algo::cstring>(elems + old_n, n_elems);
20332}
20333
20334// --- command.gcli.fields.ReadStrptrMaybe
20335// A single element is read from input string and appended to the array.
20336// If the string contains an error, the array is untouched.
20337// Function returns success value.
20338bool command::fields_ReadStrptrMaybe(command::gcli& parent, algo::strptr in_str) {
20339    bool retval = true;
20340    algo::cstring &elem = fields_Alloc(parent);
20341    retval = algo::cstring_ReadStrptrMaybe(elem, in_str);
20342    if (!retval) {
20343        fields_RemoveLast(parent);
20344    }
20345    return retval;
20346}
20347
20348// --- command.gcli..ReadFieldMaybe
20349bool command::gcli_ReadFieldMaybe(command::gcli& parent, algo::strptr field, algo::strptr strval) {
20350    bool retval = true;
20351    command::FieldId field_id;
20352    (void)value_SetStrptrMaybe(field_id,algo::Pathcomp(field, ".LL"));
20353    switch(field_id) {
20354        case command_FieldId_in: {
20355            retval = algo::cstring_ReadStrptrMaybe(parent.in, strval);
20356            break;
20357        }
20358        case command_FieldId_selector: {
20359            retval = algo::Smallstr250_ReadStrptrMaybe(parent.selector, strval);
20360            break;
20361        }
20362        case command_FieldId_fields: {
20363            retval = fields_ReadStrptrMaybe(parent, strval);
20364            break;
20365        }
20366        case command_FieldId_accept: {
20367            retval = bool_ReadStrptrMaybe(parent.accept, strval);
20368            break;
20369        }
20370        case command_FieldId_start: {
20371            retval = bool_ReadStrptrMaybe(parent.start, strval);
20372            break;
20373        }
20374        case command_FieldId_list: {
20375            retval = bool_ReadStrptrMaybe(parent.list, strval);
20376            break;
20377        }
20378        case command_FieldId_create: {
20379            retval = bool_ReadStrptrMaybe(parent.create, strval);
20380            break;
20381        }
20382        case command_FieldId_update: {
20383            retval = bool_ReadStrptrMaybe(parent.update, strval);
20384            break;
20385        }
20386        case command_FieldId_approve: {
20387            retval = bool_ReadStrptrMaybe(parent.approve, strval);
20388            break;
20389        }
20390        case command_FieldId_needs_work: {
20391            retval = bool_ReadStrptrMaybe(parent.needs_work, strval);
20392            break;
20393        }
20394        case command_FieldId_stop: {
20395            retval = bool_ReadStrptrMaybe(parent.stop, strval);
20396            break;
20397        }
20398        case command_FieldId_t: {
20399            retval = bool_ReadStrptrMaybe(parent.t, strval);
20400            break;
20401        }
20402        case command_FieldId_e: {
20403            retval = bool_ReadStrptrMaybe(parent.e, strval);
20404            break;
20405        }
20406        case command_FieldId_authdir: {
20407            retval = algo::cstring_ReadStrptrMaybe(parent.authdir, strval);
20408            break;
20409        }
20410        case command_FieldId_dry_run: {
20411            retval = bool_ReadStrptrMaybe(parent.dry_run, strval);
20412            break;
20413        }
20414        case command_FieldId_gitdir: {
20415            retval = algo::cstring_ReadStrptrMaybe(parent.gitdir, strval);
20416            break;
20417        }
20418        case command_FieldId_show_gitlab_system_notes: {
20419            retval = bool_ReadStrptrMaybe(parent.show_gitlab_system_notes, strval);
20420            break;
20421        }
20422        default: break;
20423    }
20424    if (!retval) {
20425        algo_lib::AppendErrtext("attr",field);
20426    }
20427    return retval;
20428}
20429
20430// --- command.gcli..ReadTupleMaybe
20431// Read fields of command::gcli from attributes of ascii tuple TUPLE
20432bool command::gcli_ReadTupleMaybe(command::gcli &parent, algo::Tuple &tuple) {
20433    bool retval = true;
20434    int anon_idx = 0;
20435    ind_beg(algo::Tuple_attrs_curs,attr,tuple) {
20436        if (ch_N(attr.name) == 0) {
20437            attr.name = gcli_GetAnon(parent, anon_idx++);
20438        }
20439        retval = gcli_ReadFieldMaybe(parent, attr.name, attr.value);
20440        if (!retval) {
20441            break;
20442        }
20443    }ind_end;
20444    return retval;
20445}
20446
20447// --- command.gcli..Init
20448// Set all fields to initial values.
20449void command::gcli_Init(command::gcli& parent) {
20450    parent.in = algo::strptr("data");
20451    parent.selector = algo::strptr("issue:%");
20452    parent.fields_elems 	= 0; // (command.gcli.fields)
20453    parent.fields_n     	= 0; // (command.gcli.fields)
20454    parent.fields_max   	= 0; // (command.gcli.fields)
20455    parent.accept = bool(false);
20456    parent.start = bool(false);
20457    parent.list = bool(false);
20458    parent.create = bool(false);
20459    parent.update = bool(false);
20460    parent.approve = bool(false);
20461    parent.needs_work = bool(false);
20462    parent.stop = bool(false);
20463    parent.t = bool(false);
20464    parent.e = bool(false);
20465    parent.authdir = algo::strptr(".ssim");
20466    parent.dry_run = bool(false);
20467    parent.gitdir = algo::strptr("");
20468    parent.show_gitlab_system_notes = bool(false);
20469}
20470
20471// --- command.gcli..Uninit
20472void command::gcli_Uninit(command::gcli& parent) {
20473    command::gcli &row = parent; (void)row;
20474
20475    // command.gcli.fields.Uninit (Tary)  //additional key:value pairs for use with -create, -list, -update
20476    // remove all elements from command.gcli.fields
20477    fields_RemoveAll(parent);
20478    // free memory for Tary command.gcli.fields
20479    algo_lib::malloc_FreeMem(parent.fields_elems, sizeof(algo::cstring)*parent.fields_max); // (command.gcli.fields)
20480}
20481
20482// --- command.gcli..ToCmdline
20483// Convenience function that returns a full command line
20484// Assume command is in a directory called bin
20485tempstr command::gcli_ToCmdline(command::gcli& row) {
20486    tempstr ret;
20487    ret << "bin/gcli ";
20488    gcli_PrintArgv(row, ret);
20489    // inherit less intense verbose, debug options
20490    for (int i = 1; i < algo_lib::_db.cmdline.verbose; i++) {
20491        ret << " -verbose";
20492    }
20493    for (int i = 1; i < algo_lib::_db.cmdline.debug; i++) {
20494        ret << " -debug";
20495    }
20496    return ret;
20497}
20498
20499// --- command.gcli..PrintArgv
20500// print string representation of ROW to string STR
20501// cfmt:command.gcli.Argv  printfmt:Tuple
20502void command::gcli_PrintArgv(command::gcli& row, algo::cstring& str) {
20503    algo::tempstr temp;
20504    (void)temp;
20505    (void)str;
20506    if (!(row.in == "data")) {
20507        ch_RemoveAll(temp);
20508        cstring_Print(row.in, temp);
20509        str << " -in:";
20510        strptr_PrintBash(temp,str);
20511    }
20512    ch_RemoveAll(temp);
20513    Smallstr250_Print(row.selector, temp);
20514    str << " -selector:";
20515    strptr_PrintBash(temp,str);
20516    ind_beg(gcli_fields_curs,value,row) {
20517        ch_RemoveAll(temp);
20518        cstring_Print(value, temp);
20519        str << " -fields:";
20520        strptr_PrintBash(temp,str);
20521    }ind_end;
20522    if (!(row.accept == false)) {
20523        ch_RemoveAll(temp);
20524        bool_Print(row.accept, temp);
20525        str << " -accept:";
20526        strptr_PrintBash(temp,str);
20527    }
20528    if (!(row.start == false)) {
20529        ch_RemoveAll(temp);
20530        bool_Print(row.start, temp);
20531        str << " -start:";
20532        strptr_PrintBash(temp,str);
20533    }
20534    if (!(row.list == false)) {
20535        ch_RemoveAll(temp);
20536        bool_Print(row.list, temp);
20537        str << " -list:";
20538        strptr_PrintBash(temp,str);
20539    }
20540    if (!(row.create == false)) {
20541        ch_RemoveAll(temp);
20542        bool_Print(row.create, temp);
20543        str << " -create:";
20544        strptr_PrintBash(temp,str);
20545    }
20546    if (!(row.update == false)) {
20547        ch_RemoveAll(temp);
20548        bool_Print(row.update, temp);
20549        str << " -update:";
20550        strptr_PrintBash(temp,str);
20551    }
20552    if (!(row.approve == false)) {
20553        ch_RemoveAll(temp);
20554        bool_Print(row.approve, temp);
20555        str << " -approve:";
20556        strptr_PrintBash(temp,str);
20557    }
20558    if (!(row.needs_work == false)) {
20559        ch_RemoveAll(temp);
20560        bool_Print(row.needs_work, temp);
20561        str << " -needs_work:";
20562        strptr_PrintBash(temp,str);
20563    }
20564    if (!(row.stop == false)) {
20565        ch_RemoveAll(temp);
20566        bool_Print(row.stop, temp);
20567        str << " -stop:";
20568        strptr_PrintBash(temp,str);
20569    }
20570    if (!(row.t == false)) {
20571        ch_RemoveAll(temp);
20572        bool_Print(row.t, temp);
20573        str << " -t:";
20574        strptr_PrintBash(temp,str);
20575    }
20576    if (!(row.e == false)) {
20577        ch_RemoveAll(temp);
20578        bool_Print(row.e, temp);
20579        str << " -e:";
20580        strptr_PrintBash(temp,str);
20581    }
20582    if (!(row.authdir == ".ssim")) {
20583        ch_RemoveAll(temp);
20584        cstring_Print(row.authdir, temp);
20585        str << " -authdir:";
20586        strptr_PrintBash(temp,str);
20587    }
20588    if (!(row.dry_run == false)) {
20589        ch_RemoveAll(temp);
20590        bool_Print(row.dry_run, temp);
20591        str << " -dry_run:";
20592        strptr_PrintBash(temp,str);
20593    }
20594    if (!(row.gitdir == "")) {
20595        ch_RemoveAll(temp);
20596        cstring_Print(row.gitdir, temp);
20597        str << " -gitdir:";
20598        strptr_PrintBash(temp,str);
20599    }
20600    if (!(row.show_gitlab_system_notes == false)) {
20601        ch_RemoveAll(temp);
20602        bool_Print(row.show_gitlab_system_notes, temp);
20603        str << " -show_gitlab_system_notes:";
20604        strptr_PrintBash(temp,str);
20605    }
20606}
20607
20608// --- command.gcli..GetAnon
20609algo::strptr command::gcli_GetAnon(command::gcli &parent, i32 idx) {
20610    (void)parent;//only to avoid -Wunused-parameter
20611    switch(idx) {
20612        case(0): return strptr("selector", 8);
20613        default: return strptr("fields", 6);
20614    }
20615}
20616
20617// --- command.gcli..NArgs
20618// Used with command lines
20619// Return # of command-line arguments that must follow this argument
20620// If FIELD is invalid, return -1
20621i32 command::gcli_NArgs(command::FieldId field, algo::strptr& out_dflt, bool* out_anon) {
20622    i32 retval = 1;
20623    switch (field) {
20624        case command_FieldId_in: { // $comment
20625            *out_anon = false;
20626        } break;
20627        case command_FieldId_selector: { // $comment
20628            *out_anon = true;
20629        } break;
20630        case command_FieldId_fields: { // $comment
20631            *out_anon = true;
20632        } break;
20633        case command_FieldId_accept: { // $comment
20634            *out_anon = false;
20635            retval=0;
20636            out_dflt="Y";
20637        } break;
20638        case command_FieldId_start: { // bool: no argument required but value may be specified as accept:Y
20639            *out_anon = false;
20640            retval=0;
20641            out_dflt="Y";
20642        } break;
20643        case command_FieldId_list: { // bool: no argument required but value may be specified as start:Y
20644            *out_anon = false;
20645            retval=0;
20646            out_dflt="Y";
20647        } break;
20648        case command_FieldId_create: { // bool: no argument required but value may be specified as list:Y
20649            *out_anon = false;
20650            retval=0;
20651            out_dflt="Y";
20652        } break;
20653        case command_FieldId_update: { // bool: no argument required but value may be specified as create:Y
20654            *out_anon = false;
20655            retval=0;
20656            out_dflt="Y";
20657        } break;
20658        case command_FieldId_approve: { // bool: no argument required but value may be specified as update:Y
20659            *out_anon = false;
20660            retval=0;
20661            out_dflt="Y";
20662        } break;
20663        case command_FieldId_needs_work: { // bool: no argument required but value may be specified as approve:Y
20664            *out_anon = false;
20665            retval=0;
20666            out_dflt="Y";
20667        } break;
20668        case command_FieldId_stop: { // bool: no argument required but value may be specified as needs_work:Y
20669            *out_anon = false;
20670            retval=0;
20671            out_dflt="Y";
20672        } break;
20673        case command_FieldId_t: { // bool: no argument required but value may be specified as stop:Y
20674            *out_anon = false;
20675            retval=0;
20676            out_dflt="Y";
20677        } break;
20678        case command_FieldId_e: { // bool: no argument required but value may be specified as t:Y
20679            *out_anon = false;
20680            retval=0;
20681            out_dflt="Y";
20682        } break;
20683        case command_FieldId_authdir: { // bool: no argument required but value may be specified as e:Y
20684            *out_anon = false;
20685        } break;
20686        case command_FieldId_dry_run: { // bool: no argument required but value may be specified as e:Y
20687            *out_anon = false;
20688            retval=0;
20689            out_dflt="Y";
20690        } break;
20691        case command_FieldId_gitdir: { // bool: no argument required but value may be specified as dry_run:Y
20692            *out_anon = false;
20693        } break;
20694        case command_FieldId_show_gitlab_system_notes: { // bool: no argument required but value may be specified as dry_run:Y
20695            *out_anon = false;
20696            retval=0;
20697            out_dflt="Y";
20698        } break;
20699        default:
20700        retval=-1; // unrecognized
20701    }
20702    return retval;
20703}
20704
20705// --- command.gcli_proc.gcli.Start
20706// Start subprocess
20707// If subprocess already running, do nothing. Otherwise, start it
20708int command::gcli_Start(command::gcli_proc& parent) {
20709    int retval = 0;
20710    if (parent.pid == 0) {
20711        verblog(gcli_ToCmdline(parent)); // maybe print command
20712#ifdef WIN32
20713        algo_lib::ResolveExecFname(parent.path);
20714        tempstr cmdline(gcli_ToCmdline(parent));
20715        parent.pid = dospawn(Zeroterm(parent.path),Zeroterm(cmdline),parent.timeout,parent.fstdin,parent.fstdout,parent.fstderr);
20716#else
20717        parent.pid = fork();
20718        if (parent.pid == 0) { // child
20719            algo_lib::DieWithParent();
20720            if (parent.timeout > 0) {
20721                alarm(parent.timeout);
20722            }
20723            if (retval==0) retval=algo_lib::ApplyRedirect(parent.fstdin , 0);
20724            if (retval==0) retval=algo_lib::ApplyRedirect(parent.fstdout, 1);
20725            if (retval==0) retval=algo_lib::ApplyRedirect(parent.fstderr, 2);
20726            if (retval==0) retval= gcli_Execv(parent);
20727            if (retval != 0) { // if start fails, print error
20728                int err=errno;
20729                prerr("command.gcli_execv"
20730                <<Keyval("errno",err)
20731                <<Keyval("errstr",strerror(err))
20732                <<Keyval("comment","Execv failed"));
20733            }
20734            _exit(127); // if failed to start, exit anyway
20735        } else if (parent.pid == -1) {
20736            retval = errno; // failed to fork
20737        }
20738#endif
20739    }
20740    parent.status = parent.pid > 0 ? 0 : -1; // if didn't start, set error status
20741    return retval;
20742}
20743
20744// --- command.gcli_proc.gcli.StartRead
20745// Start subprocess & Read output
20746algo::Fildes command::gcli_StartRead(command::gcli_proc& parent, algo_lib::FFildes &read) {
20747    int pipefd[2];
20748    int rc=pipe(pipefd);
20749    (void)rc;
20750    read.fd.value = pipefd[0];
20751    parent.fstdout  << ">&" << pipefd[1];
20752    gcli_Start(parent);
20753    (void)close(pipefd[1]);
20754    return read.fd;
20755}
20756
20757// --- command.gcli_proc.gcli.Kill
20758// Kill subprocess and wait
20759void command::gcli_Kill(command::gcli_proc& parent) {
20760    if (parent.pid != 0) {
20761        kill(parent.pid,9);
20762        gcli_Wait(parent);
20763    }
20764}
20765
20766// --- command.gcli_proc.gcli.Wait
20767// Wait for subprocess to return
20768void command::gcli_Wait(command::gcli_proc& parent) {
20769    if (parent.pid > 0) {
20770        int wait_flags = 0;
20771        int wait_status = 0;
20772        int rc = -1;
20773        do {
20774            // really wait for subprocess to exit
20775            rc = waitpid(parent.pid,&wait_status,wait_flags);
20776        } while (rc==-1 && errno==EINTR);
20777        if (rc == parent.pid) {
20778            parent.status = wait_status;
20779            parent.pid = 0;
20780        }
20781    }
20782}
20783
20784// --- command.gcli_proc.gcli.Exec
20785// Start + Wait
20786// Execute subprocess and return exit code
20787int command::gcli_Exec(command::gcli_proc& parent) {
20788    gcli_Start(parent);
20789    gcli_Wait(parent);
20790    return parent.status;
20791}
20792
20793// --- command.gcli_proc.gcli.ExecX
20794// Start + Wait, throw exception on error
20795// Execute subprocess; throw human-readable exception on error
20796void command::gcli_ExecX(command::gcli_proc& parent) {
20797    int rc = gcli_Exec(parent);
20798    vrfy(rc==0, tempstr() << "algo_lib.exec" << Keyval("cmd",gcli_ToCmdline(parent))
20799    << Keyval("comment",algo::DescribeWaitStatus(parent.status)));
20800}
20801
20802// --- command.gcli_proc.gcli.Execv
20803// Call execv()
20804// Call execv with specified parameters
20805int command::gcli_Execv(command::gcli_proc& parent) {
20806    int ret = 0;
20807    algo::StringAry args;
20808    gcli_ToArgv(parent, args);
20809    char **argv = (char**)alloca((ary_N(args)+1)*sizeof(*argv));
20810    ind_beg(algo::StringAry_ary_curs,arg,args) {
20811        argv[ind_curs(arg).index] = Zeroterm(arg);
20812    }ind_end;
20813    argv[ary_N(args)] = NULL;
20814    // if parent.path is relative, search for it in PATH
20815    algo_lib::ResolveExecFname(parent.path);
20816    ret = execv(Zeroterm(parent.path),argv);
20817    return ret;
20818}
20819
20820// --- command.gcli_proc.gcli.ToCmdline
20821algo::tempstr command::gcli_ToCmdline(command::gcli_proc& parent) {
20822    algo::tempstr retval;
20823    retval << parent.path << " ";
20824    command::gcli_PrintArgv(parent.cmd,retval);
20825    if (ch_N(parent.fstdin)) {
20826        retval << " " << parent.fstdin;
20827    }
20828    if (ch_N(parent.fstdout)) {
20829        retval << " " << parent.fstdout;
20830    }
20831    if (ch_N(parent.fstderr)) {
20832        retval << " 2" << parent.fstderr;
20833    }
20834    return retval;
20835}
20836
20837// --- command.gcli_proc.gcli.ToArgv
20838// Form array from the command line
20839void command::gcli_ToArgv(command::gcli_proc& parent, algo::StringAry& args) {
20840    ary_RemoveAll(args);
20841    ary_Alloc(args) << parent.path;
20842
20843    if (parent.cmd.in != "data") {
20844        cstring *arg = &ary_Alloc(args);
20845        *arg << "-in:";
20846        cstring_Print(parent.cmd.in, *arg);
20847    }
20848
20849    if (parent.cmd.selector != "issue:%") {
20850        cstring *arg = &ary_Alloc(args);
20851        *arg << "-selector:";
20852        Smallstr250_Print(parent.cmd.selector, *arg);
20853    }
20854    ind_beg(command::gcli_fields_curs,value,parent.cmd) {
20855        cstring *arg = &ary_Alloc(args);
20856        *arg << "-fields:";
20857        cstring_Print(value, *arg);
20858    }ind_end;
20859
20860    if (parent.cmd.accept != false) {
20861        cstring *arg = &ary_Alloc(args);
20862        *arg << "-accept:";
20863        bool_Print(parent.cmd.accept, *arg);
20864    }
20865
20866    if (parent.cmd.start != false) {
20867        cstring *arg = &ary_Alloc(args);
20868        *arg << "-start:";
20869        bool_Print(parent.cmd.start, *arg);
20870    }
20871
20872    if (parent.cmd.list != false) {
20873        cstring *arg = &ary_Alloc(args);
20874        *arg << "-list:";
20875        bool_Print(parent.cmd.list, *arg);
20876    }
20877
20878    if (parent.cmd.create != false) {
20879        cstring *arg = &ary_Alloc(args);
20880        *arg << "-create:";
20881        bool_Print(parent.cmd.create, *arg);
20882    }
20883
20884    if (parent.cmd.update != false) {
20885        cstring *arg = &ary_Alloc(args);
20886        *arg << "-update:";
20887        bool_Print(parent.cmd.update, *arg);
20888    }
20889
20890    if (parent.cmd.approve != false) {
20891        cstring *arg = &ary_Alloc(args);
20892        *arg << "-approve:";
20893        bool_Print(parent.cmd.approve, *arg);
20894    }
20895
20896    if (parent.cmd.needs_work != false) {
20897        cstring *arg = &ary_Alloc(args);
20898        *arg << "-needs_work:";
20899        bool_Print(parent.cmd.needs_work, *arg);
20900    }
20901
20902    if (parent.cmd.stop != false) {
20903        cstring *arg = &ary_Alloc(args);
20904        *arg << "-stop:";
20905        bool_Print(parent.cmd.stop, *arg);
20906    }
20907
20908    if (parent.cmd.t != false) {
20909        cstring *arg = &ary_Alloc(args);
20910        *arg << "-t:";
20911        bool_Print(parent.cmd.t, *arg);
20912    }
20913
20914    if (parent.cmd.e != false) {
20915        cstring *arg = &ary_Alloc(args);
20916        *arg << "-e:";
20917        bool_Print(parent.cmd.e, *arg);
20918    }
20919
20920    if (parent.cmd.authdir != ".ssim") {
20921        cstring *arg = &ary_Alloc(args);
20922        *arg << "-authdir:";
20923        cstring_Print(parent.cmd.authdir, *arg);
20924    }
20925
20926    if (parent.cmd.dry_run != false) {
20927        cstring *arg = &ary_Alloc(args);
20928        *arg << "-dry_run:";
20929        bool_Print(parent.cmd.dry_run, *arg);
20930    }
20931
20932    if (parent.cmd.gitdir != "") {
20933        cstring *arg = &ary_Alloc(args);
20934        *arg << "-gitdir:";
20935        cstring_Print(parent.cmd.gitdir, *arg);
20936    }
20937
20938    if (parent.cmd.show_gitlab_system_notes != false) {
20939        cstring *arg = &ary_Alloc(args);
20940        *arg << "-show_gitlab_system_notes:";
20941        bool_Print(parent.cmd.show_gitlab_system_notes, *arg);
20942    }
20943    for (int i=1; i < algo_lib::_db.cmdline.verbose; ++i) {
20944        ary_Alloc(args) << "-verbose";
20945    }
20946}
20947
20948// --- command.gcli_proc..Uninit
20949void command::gcli_proc_Uninit(command::gcli_proc& parent) {
20950    command::gcli_proc &row = parent; (void)row;
20951
20952    // command.gcli_proc.gcli.Uninit (Exec)  //
20953    gcli_Kill(parent); // kill child, ensure forward progress
20954}
20955
20956// --- command.gmv.gmvproj.Print
20957// Print back to string
20958void command::gmvproj_Print(command::gmv& parent, algo::cstring &out) {
20959    Regx_Print(parent.gmvproj, out);
20960}
20961
20962// --- command.gmv.gmvproj.ReadStrptrMaybe
20963// Read Regx from string
20964// Convert string to field. Return success value
20965bool command::gmvproj_ReadStrptrMaybe(command::gmv& parent, algo::strptr in) {
20966    bool retval = true;
20967    Regx_ReadSql(parent.gmvproj, in, true);
20968    return retval;
20969}
20970
20971// --- command.gmv..ReadFieldMaybe
20972bool command::gmv_ReadFieldMaybe(command::gmv& parent, algo::strptr field, algo::strptr strval) {
20973    bool retval = true;
20974    command::FieldId field_id;
20975    (void)value_SetStrptrMaybe(field_id,field);
20976    switch(field_id) {
20977        case command_FieldId_in: {
20978            retval = algo::cstring_ReadStrptrMaybe(parent.in, strval);
20979            break;
20980        }
20981        case command_FieldId_gmvproj: {
20982            retval = gmvproj_ReadStrptrMaybe(parent, strval);
20983            break;
20984        }
20985        case command_FieldId_move_out: {
20986            retval = bool_ReadStrptrMaybe(parent.move_out, strval);
20987            break;
20988        }
20989        case command_FieldId_move_in: {
20990            retval = bool_ReadStrptrMaybe(parent.move_in, strval);
20991            break;
20992        }
20993        case command_FieldId_commit: {
20994            retval = bool_ReadStrptrMaybe(parent.commit, strval);
20995            break;
20996        }
20997        case command_FieldId_no_rebase: {
20998            retval = bool_ReadStrptrMaybe(parent.no_rebase, strval);
20999            break;
21000        }
21001        case command_FieldId_dry_run: {
21002            retval = bool_ReadStrptrMaybe(parent.dry_run, strval);
21003            break;
21004        }
21005        default: break;
21006    }
21007    if (!retval) {
21008        algo_lib::AppendErrtext("attr",field);
21009    }
21010    return retval;
21011}
21012
21013// --- command.gmv..ReadTupleMaybe
21014// Read fields of command::gmv from attributes of ascii tuple TUPLE
21015bool command::gmv_ReadTupleMaybe(command::gmv &parent, algo::Tuple &tuple) {
21016    bool retval = true;
21017    ind_beg(algo::Tuple_attrs_curs,attr,tuple) {
21018        retval = gmv_ReadFieldMaybe(parent, attr.name, attr.value);
21019        if (!retval) {
21020            break;
21021        }
21022    }ind_end;
21023    return retval;
21024}
21025
21026// --- command.gmv..Init
21027// Set all fields to initial values.
21028void command::gmv_Init(command::gmv& parent) {
21029    parent.in = algo::strptr("data");
21030    Regx_ReadSql(parent.gmvproj, "wiki-algornd", true);
21031    parent.move_out = bool(false);
21032    parent.move_in = bool(false);
21033    parent.commit = bool(false);
21034    parent.no_rebase = bool(false);
21035    parent.dry_run = bool(false);
21036}
21037
21038// --- command.gmv..ToCmdline
21039// Convenience function that returns a full command line
21040// Assume command is in a directory called bin
21041tempstr command::gmv_ToCmdline(command::gmv& row) {
21042    tempstr ret;
21043    ret << "bin/gmv ";
21044    gmv_PrintArgv(row, ret);
21045    // inherit less intense verbose, debug options
21046    for (int i = 1; i < algo_lib::_db.cmdline.verbose; i++) {
21047        ret << " -verbose";
21048    }
21049    for (int i = 1; i < algo_lib::_db.cmdline.debug; i++) {
21050        ret << " -debug";
21051    }
21052    return ret;
21053}
21054
21055// --- command.gmv..PrintArgv
21056// print string representation of ROW to string STR
21057// cfmt:command.gmv.Argv  printfmt:Tuple
21058void command::gmv_PrintArgv(command::gmv& row, algo::cstring& str) {
21059    algo::tempstr temp;
21060    (void)temp;
21061    (void)str;
21062    if (!(row.in == "data")) {
21063        ch_RemoveAll(temp);
21064        cstring_Print(row.in, temp);
21065        str << " -in:";
21066        strptr_PrintBash(temp,str);
21067    }
21068    if (!(row.gmvproj.expr == "wiki-algornd")) {
21069        ch_RemoveAll(temp);
21070        command::gmvproj_Print(const_cast<command::gmv&>(row), temp);
21071        str << " -gmvproj:";
21072        strptr_PrintBash(temp,str);
21073    }
21074    if (!(row.move_out == false)) {
21075        ch_RemoveAll(temp);
21076        bool_Print(row.move_out, temp);
21077        str << " -move_out:";
21078        strptr_PrintBash(temp,str);
21079    }
21080    if (!(row.move_in == false)) {
21081        ch_RemoveAll(temp);
21082        bool_Print(row.move_in, temp);
21083        str << " -move_in:";
21084        strptr_PrintBash(temp,str);
21085    }
21086    if (!(row.commit == false)) {
21087        ch_RemoveAll(temp);
21088        bool_Print(row.commit, temp);
21089        str << " -commit:";
21090        strptr_PrintBash(temp,str);
21091    }
21092    if (!(row.no_rebase == false)) {
21093        ch_RemoveAll(temp);
21094        bool_Print(row.no_rebase, temp);
21095        str << " -no_rebase:";
21096        strptr_PrintBash(temp,str);
21097    }
21098    if (!(row.dry_run == false)) {
21099        ch_RemoveAll(temp);
21100        bool_Print(row.dry_run, temp);
21101        str << " -dry_run:";
21102        strptr_PrintBash(temp,str);
21103    }
21104}
21105
21106// --- command.gmv..NArgs
21107// Used with command lines
21108// Return # of command-line arguments that must follow this argument
21109// If FIELD is invalid, return -1
21110i32 command::gmv_NArgs(command::FieldId field, algo::strptr& out_dflt, bool* out_anon) {
21111    i32 retval = 1;
21112    switch (field) {
21113        case command_FieldId_in: { // $comment
21114            *out_anon = false;
21115        } break;
21116        case command_FieldId_gmvproj: { // $comment
21117            *out_anon = false;
21118        } break;
21119        case command_FieldId_move_out: { // $comment
21120            *out_anon = false;
21121            retval=0;
21122            out_dflt="Y";
21123        } break;
21124        case command_FieldId_move_in: { // bool: no argument required but value may be specified as move_out:Y
21125            *out_anon = false;
21126            retval=0;
21127            out_dflt="Y";
21128        } break;
21129        case command_FieldId_commit: { // bool: no argument required but value may be specified as move_in:Y
21130            *out_anon = false;
21131            retval=0;
21132            out_dflt="Y";
21133        } break;
21134        case command_FieldId_no_rebase: { // bool: no argument required but value may be specified as commit:Y
21135            *out_anon = false;
21136            retval=0;
21137            out_dflt="Y";
21138        } break;
21139        case command_FieldId_dry_run: { // bool: no argument required but value may be specified as no_rebase:Y
21140            *out_anon = false;
21141            retval=0;
21142            out_dflt="Y";
21143        } break;
21144        default:
21145        retval=-1; // unrecognized
21146    }
21147    return retval;
21148}
21149
21150// --- command.gmv_proc.gmv.Start
21151// Start subprocess
21152// If subprocess already running, do nothing. Otherwise, start it
21153int command::gmv_Start(command::gmv_proc& parent) {
21154    int retval = 0;
21155    if (parent.pid == 0) {
21156        verblog(gmv_ToCmdline(parent)); // maybe print command
21157#ifdef WIN32
21158        algo_lib::ResolveExecFname(parent.path);
21159        tempstr cmdline(gmv_ToCmdline(parent));
21160        parent.pid = dospawn(Zeroterm(parent.path),Zeroterm(cmdline),parent.timeout,parent.fstdin,parent.fstdout,parent.fstderr);
21161#else
21162        parent.pid = fork();
21163        if (parent.pid == 0) { // child
21164            algo_lib::DieWithParent();
21165            if (parent.timeout > 0) {
21166                alarm(parent.timeout);
21167            }
21168            if (retval==0) retval=algo_lib::ApplyRedirect(parent.fstdin , 0);
21169            if (retval==0) retval=algo_lib::ApplyRedirect(parent.fstdout, 1);
21170            if (retval==0) retval=algo_lib::ApplyRedirect(parent.fstderr, 2);
21171            if (retval==0) retval= gmv_Execv(parent);
21172            if (retval != 0) { // if start fails, print error
21173                int err=errno;
21174                prerr("command.gmv_execv"
21175                <<Keyval("errno",err)
21176                <<Keyval("errstr",strerror(err))
21177                <<Keyval("comment","Execv failed"));
21178            }
21179            _exit(127); // if failed to start, exit anyway
21180        } else if (parent.pid == -1) {
21181            retval = errno; // failed to fork
21182        }
21183#endif
21184    }
21185    parent.status = parent.pid > 0 ? 0 : -1; // if didn't start, set error status
21186    return retval;
21187}
21188
21189// --- command.gmv_proc.gmv.StartRead
21190// Start subprocess & Read output
21191algo::Fildes command::gmv_StartRead(command::gmv_proc& parent, algo_lib::FFildes &read) {
21192    int pipefd[2];
21193    int rc=pipe(pipefd);
21194    (void)rc;
21195    read.fd.value = pipefd[0];
21196    parent.fstdout  << ">&" << pipefd[1];
21197    gmv_Start(parent);
21198    (void)close(pipefd[1]);
21199    return read.fd;
21200}
21201
21202// --- command.gmv_proc.gmv.Kill
21203// Kill subprocess and wait
21204void command::gmv_Kill(command::gmv_proc& parent) {
21205    if (parent.pid != 0) {
21206        kill(parent.pid,9);
21207        gmv_Wait(parent);
21208    }
21209}
21210
21211// --- command.gmv_proc.gmv.Wait
21212// Wait for subprocess to return
21213void command::gmv_Wait(command::gmv_proc& parent) {
21214    if (parent.pid > 0) {
21215        int wait_flags = 0;
21216        int wait_status = 0;
21217        int rc = -1;
21218        do {
21219            // really wait for subprocess to exit
21220            rc = waitpid(parent.pid,&wait_status,wait_flags);
21221        } while (rc==-1 && errno==EINTR);
21222        if (rc == parent.pid) {
21223            parent.status = wait_status;
21224            parent.pid = 0;
21225        }
21226    }
21227}
21228
21229// --- command.gmv_proc.gmv.Exec
21230// Start + Wait
21231// Execute subprocess and return exit code
21232int command::gmv_Exec(command::gmv_proc& parent) {
21233    gmv_Start(parent);
21234    gmv_Wait(parent);
21235    return parent.status;
21236}
21237
21238// --- command.gmv_proc.gmv.ExecX
21239// Start + Wait, throw exception on error
21240// Execute subprocess; throw human-readable exception on error
21241void command::gmv_ExecX(command::gmv_proc& parent) {
21242    int rc = gmv_Exec(parent);
21243    vrfy(rc==0, tempstr() << "algo_lib.exec" << Keyval("cmd",gmv_ToCmdline(parent))
21244    << Keyval("comment",algo::DescribeWaitStatus(parent.status)));
21245}
21246
21247// --- command.gmv_proc.gmv.Execv
21248// Call execv()
21249// Call execv with specified parameters
21250int command::gmv_Execv(command::gmv_proc& parent) {
21251    int ret = 0;
21252    algo::StringAry args;
21253    gmv_ToArgv(parent, args);
21254    char **argv = (char**)alloca((ary_N(args)+1)*sizeof(*argv));
21255    ind_beg(algo::StringAry_ary_curs,arg,args) {
21256        argv[ind_curs(arg).index] = Zeroterm(arg);
21257    }ind_end;
21258    argv[ary_N(args)] = NULL;
21259    // if parent.path is relative, search for it in PATH
21260    algo_lib::ResolveExecFname(parent.path);
21261    ret = execv(Zeroterm(parent.path),argv);
21262    return ret;
21263}
21264
21265// --- command.gmv_proc.gmv.ToCmdline
21266algo::tempstr command::gmv_ToCmdline(command::gmv_proc& parent) {
21267    algo::tempstr retval;
21268    retval << parent.path << " ";
21269    command::gmv_PrintArgv(parent.cmd,retval);
21270    if (ch_N(parent.fstdin)) {
21271        retval << " " << parent.fstdin;
21272    }
21273    if (ch_N(parent.fstdout)) {
21274        retval << " " << parent.fstdout;
21275    }
21276    if (ch_N(parent.fstderr)) {
21277        retval << " 2" << parent.fstderr;
21278    }
21279    return retval;
21280}
21281
21282// --- command.gmv_proc.gmv.ToArgv
21283// Form array from the command line
21284void command::gmv_ToArgv(command::gmv_proc& parent, algo::StringAry& args) {
21285    ary_RemoveAll(args);
21286    ary_Alloc(args) << parent.path;
21287
21288    if (parent.cmd.in != "data") {
21289        cstring *arg = &ary_Alloc(args);
21290        *arg << "-in:";
21291        cstring_Print(parent.cmd.in, *arg);
21292    }
21293
21294    if (parent.cmd.gmvproj.expr != "wiki-algornd") {
21295        cstring *arg = &ary_Alloc(args);
21296        *arg << "-gmvproj:";
21297        command::gmvproj_Print(parent.cmd, *arg);
21298    }
21299
21300    if (parent.cmd.move_out != false) {
21301        cstring *arg = &ary_Alloc(args);
21302        *arg << "-move_out:";
21303        bool_Print(parent.cmd.move_out, *arg);
21304    }
21305
21306    if (parent.cmd.move_in != false) {
21307        cstring *arg = &ary_Alloc(args);
21308        *arg << "-move_in:";
21309        bool_Print(parent.cmd.move_in, *arg);
21310    }
21311
21312    if (parent.cmd.commit != false) {
21313        cstring *arg = &ary_Alloc(args);
21314        *arg << "-commit:";
21315        bool_Print(parent.cmd.commit, *arg);
21316    }
21317
21318    if (parent.cmd.no_rebase != false) {
21319        cstring *arg = &ary_Alloc(args);
21320        *arg << "-no_rebase:";
21321        bool_Print(parent.cmd.no_rebase, *arg);
21322    }
21323
21324    if (parent.cmd.dry_run != false) {
21325        cstring *arg = &ary_Alloc(args);
21326        *arg << "-dry_run:";
21327        bool_Print(parent.cmd.dry_run, *arg);
21328    }
21329    for (int i=1; i < algo_lib::_db.cmdline.verbose; ++i) {
21330        ary_Alloc(args) << "-verbose";
21331    }
21332}
21333
21334// --- command.gmv_proc..Uninit
21335void command::gmv_proc_Uninit(command::gmv_proc& parent) {
21336    command::gmv_proc &row = parent; (void)row;
21337
21338    // command.gmv_proc.gmv.Uninit (Exec)  //
21339    gmv_Kill(parent); // kill child, ensure forward progress
21340}
21341
21342// --- command.mdbg.args.Addary
21343// Reserve space (this may move memory). Insert N element at the end.
21344// Return aryptr to newly inserted block.
21345// If the RHS argument aliases the array (refers to the same memory), exit program with fatal error.
21346algo::aryptr<algo::cstring> command::args_Addary(command::mdbg& parent, algo::aryptr<algo::cstring> rhs) {
21347    bool overlaps = rhs.n_elems>0 && rhs.elems >= parent.args_elems && rhs.elems < parent.args_elems + parent.args_max;
21348    if (UNLIKELY(overlaps)) {
21349        FatalErrorExit("command.tary_alias  field:command.mdbg.args  comment:'alias error: sub-array is being appended to the whole'");
21350    }
21351    int nnew = rhs.n_elems;
21352    args_Reserve(parent, nnew); // reserve space
21353    int at = parent.args_n;
21354    for (int i = 0; i < nnew; i++) {
21355        new (parent.args_elems + at + i) algo::cstring(rhs[i]);
21356        parent.args_n++;
21357    }
21358    return algo::aryptr<algo::cstring>(parent.args_elems + at, nnew);
21359}
21360
21361// --- command.mdbg.args.Alloc
21362// Reserve space. Insert element at the end
21363// The new element is initialized to a default value
21364algo::cstring& command::args_Alloc(command::mdbg& parent) {
21365    args_Reserve(parent, 1);
21366    int n  = parent.args_n;
21367    int at = n;
21368    algo::cstring *elems = parent.args_elems;
21369    new (elems + at) algo::cstring(""); // construct new element, default initializer
21370    parent.args_n = n+1;
21371    return elems[at];
21372}
21373
21374// --- command.mdbg.args.AllocAt
21375// Reserve space for new element, reallocating the array if necessary
21376// Insert new element at specified index. Index must be in range or a fatal error occurs.
21377algo::cstring& command::args_AllocAt(command::mdbg& parent, int at) {
21378    args_Reserve(parent, 1);
21379    int n  = parent.args_n;
21380    if (UNLIKELY(u64(at) >= u64(n+1))) {
21381        FatalErrorExit("command.bad_alloc_at  field:command.mdbg.args  comment:'index out of range'");
21382    }
21383    algo::cstring *elems = parent.args_elems;
21384    memmove(elems + at + 1, elems + at, (n - at) * sizeof(algo::cstring));
21385    new (elems + at) algo::cstring(""); // construct element, default initializer
21386    parent.args_n = n+1;
21387    return elems[at];
21388}
21389
21390// --- command.mdbg.args.AllocN
21391// Reserve space. Insert N elements at the end of the array, return pointer to array
21392algo::aryptr<algo::cstring> command::args_AllocN(command::mdbg& parent, int n_elems) {
21393    args_Reserve(parent, n_elems);
21394    int old_n  = parent.args_n;
21395    int new_n = old_n + n_elems;
21396    algo::cstring *elems = parent.args_elems;
21397    for (int i = old_n; i < new_n; i++) {
21398        new (elems + i) algo::cstring(""); // construct new element, default initialize
21399    }
21400    parent.args_n = new_n;
21401    return algo::aryptr<algo::cstring>(elems + old_n, n_elems);
21402}
21403
21404// --- command.mdbg.args.Remove
21405// Remove item by index. If index outside of range, do nothing.
21406void command::args_Remove(command::mdbg& parent, u32 i) {
21407    u32 lim = parent.args_n;
21408    algo::cstring *elems = parent.args_elems;
21409    if (i < lim) {
21410        elems[i].~cstring(); // destroy element
21411        memmove(elems + i, elems + (i + 1), sizeof(algo::cstring) * (lim - (i + 1)));
21412        parent.args_n = lim - 1;
21413    }
21414}
21415
21416// --- command.mdbg.args.RemoveAll
21417void command::args_RemoveAll(command::mdbg& parent) {
21418    u32 n = parent.args_n;
21419    while (n > 0) {
21420        n -= 1;
21421        parent.args_elems[n].~cstring();
21422        parent.args_n = n;
21423    }
21424}
21425
21426// --- command.mdbg.args.RemoveLast
21427// Delete last element of array. Do nothing if array is empty.
21428void command::args_RemoveLast(command::mdbg& parent) {
21429    u64 n = parent.args_n;
21430    if (n > 0) {
21431        n -= 1;
21432        args_qFind(parent, u64(n)).~cstring();
21433        parent.args_n = n;
21434    }
21435}
21436
21437// --- command.mdbg.args.AbsReserve
21438// Make sure N elements fit in array. Process dies if out of memory
21439void command::args_AbsReserve(command::mdbg& parent, int n) {
21440    u32 old_max  = parent.args_max;
21441    if (n > i32(old_max)) {
21442        u32 new_max  = i32_Max(i32_Max(old_max * 2, n), 4);
21443        void *new_mem = algo_lib::malloc_ReallocMem(parent.args_elems, old_max * sizeof(algo::cstring), new_max * sizeof(algo::cstring));
21444        if (UNLIKELY(!new_mem)) {
21445            FatalErrorExit("command.tary_nomem  field:command.mdbg.args  comment:'out of memory'");
21446        }
21447        parent.args_elems = (algo::cstring*)new_mem;
21448        parent.args_max = new_max;
21449    }
21450}
21451
21452// --- command.mdbg.args.Setary
21453// Copy contents of RHS to PARENT.
21454void command::args_Setary(command::mdbg& parent, command::mdbg &rhs) {
21455    args_RemoveAll(parent);
21456    int nnew = rhs.args_n;
21457    args_Reserve(parent, nnew); // reserve space
21458    for (int i = 0; i < nnew; i++) { // copy elements over
21459        new (parent.args_elems + i) algo::cstring(args_qFind(rhs, i));
21460        parent.args_n = i + 1;
21461    }
21462}
21463
21464// --- command.mdbg.args.Setary2
21465// Copy specified array into args, discarding previous contents.
21466// If the RHS argument aliases the array (refers to the same memory), throw exception.
21467void command::args_Setary(command::mdbg& parent, const algo::aryptr<algo::cstring> &rhs) {
21468    args_RemoveAll(parent);
21469    args_Addary(parent, rhs);
21470}
21471
21472// --- command.mdbg.args.AllocNVal
21473// Reserve space. Insert N elements at the end of the array, return pointer to array
21474algo::aryptr<algo::cstring> command::args_AllocNVal(command::mdbg& parent, int n_elems, const algo::cstring& val) {
21475    args_Reserve(parent, n_elems);
21476    int old_n  = parent.args_n;
21477    int new_n = old_n + n_elems;
21478    algo::cstring *elems = parent.args_elems;
21479    for (int i = old_n; i < new_n; i++) {
21480        new (elems + i) algo::cstring(val);
21481    }
21482    parent.args_n = new_n;
21483    return algo::aryptr<algo::cstring>(elems + old_n, n_elems);
21484}
21485
21486// --- command.mdbg.args.ReadStrptrMaybe
21487// A single element is read from input string and appended to the array.
21488// If the string contains an error, the array is untouched.
21489// Function returns success value.
21490bool command::args_ReadStrptrMaybe(command::mdbg& parent, algo::strptr in_str) {
21491    bool retval = true;
21492    algo::cstring &elem = args_Alloc(parent);
21493    retval = algo::cstring_ReadStrptrMaybe(elem, in_str);
21494    if (!retval) {
21495        args_RemoveLast(parent);
21496    }
21497    return retval;
21498}
21499
21500// --- command.mdbg.b.Addary
21501// Reserve space (this may move memory). Insert N element at the end.
21502// Return aryptr to newly inserted block.
21503// If the RHS argument aliases the array (refers to the same memory), exit program with fatal error.
21504algo::aryptr<algo::cstring> command::b_Addary(command::mdbg& parent, algo::aryptr<algo::cstring> rhs) {
21505    bool overlaps = rhs.n_elems>0 && rhs.elems >= parent.b_elems && rhs.elems < parent.b_elems + parent.b_max;
21506    if (UNLIKELY(overlaps)) {
21507        FatalErrorExit("command.tary_alias  field:command.mdbg.b  comment:'alias error: sub-array is being appended to the whole'");
21508    }
21509    int nnew = rhs.n_elems;
21510    b_Reserve(parent, nnew); // reserve space
21511    int at = parent.b_n;
21512    for (int i = 0; i < nnew; i++) {
21513        new (parent.b_elems + at + i) algo::cstring(rhs[i]);
21514        parent.b_n++;
21515    }
21516    return algo::aryptr<algo::cstring>(parent.b_elems + at, nnew);
21517}
21518
21519// --- command.mdbg.b.Alloc
21520// Reserve space. Insert element at the end
21521// The new element is initialized to a default value
21522algo::cstring& command::b_Alloc(command::mdbg& parent) {
21523    b_Reserve(parent, 1);
21524    int n  = parent.b_n;
21525    int at = n;
21526    algo::cstring *elems = parent.b_elems;
21527    new (elems + at) algo::cstring(""); // construct new element, default initializer
21528    parent.b_n = n+1;
21529    return elems[at];
21530}
21531
21532// --- command.mdbg.b.AllocAt
21533// Reserve space for new element, reallocating the array if necessary
21534// Insert new element at specified index. Index must be in range or a fatal error occurs.
21535algo::cstring& command::b_AllocAt(command::mdbg& parent, int at) {
21536    b_Reserve(parent, 1);
21537    int n  = parent.b_n;
21538    if (UNLIKELY(u64(at) >= u64(n+1))) {
21539        FatalErrorExit("command.bad_alloc_at  field:command.mdbg.b  comment:'index out of range'");
21540    }
21541    algo::cstring *elems = parent.b_elems;
21542    memmove(elems + at + 1, elems + at, (n - at) * sizeof(algo::cstring));
21543    new (elems + at) algo::cstring(""); // construct element, default initializer
21544    parent.b_n = n+1;
21545    return elems[at];
21546}
21547
21548// --- command.mdbg.b.AllocN
21549// Reserve space. Insert N elements at the end of the array, return pointer to array
21550algo::aryptr<algo::cstring> command::b_AllocN(command::mdbg& parent, int n_elems) {
21551    b_Reserve(parent, n_elems);
21552    int old_n  = parent.b_n;
21553    int new_n = old_n + n_elems;
21554    algo::cstring *elems = parent.b_elems;
21555    for (int i = old_n; i < new_n; i++) {
21556        new (elems + i) algo::cstring(""); // construct new element, default initialize
21557    }
21558    parent.b_n = new_n;
21559    return algo::aryptr<algo::cstring>(elems + old_n, n_elems);
21560}
21561
21562// --- command.mdbg.b.Remove
21563// Remove item by index. If index outside of range, do nothing.
21564void command::b_Remove(command::mdbg& parent, u32 i) {
21565    u32 lim = parent.b_n;
21566    algo::cstring *elems = parent.b_elems;
21567    if (i < lim) {
21568        elems[i].~cstring(); // destroy element
21569        memmove(elems + i, elems + (i + 1), sizeof(algo::cstring) * (lim - (i + 1)));
21570        parent.b_n = lim - 1;
21571    }
21572}
21573
21574// --- command.mdbg.b.RemoveAll
21575void command::b_RemoveAll(command::mdbg& parent) {
21576    u32 n = parent.b_n;
21577    while (n > 0) {
21578        n -= 1;
21579        parent.b_elems[n].~cstring();
21580        parent.b_n = n;
21581    }
21582}
21583
21584// --- command.mdbg.b.RemoveLast
21585// Delete last element of array. Do nothing if array is empty.
21586void command::b_RemoveLast(command::mdbg& parent) {
21587    u64 n = parent.b_n;
21588    if (n > 0) {
21589        n -= 1;
21590        b_qFind(parent, u64(n)).~cstring();
21591        parent.b_n = n;
21592    }
21593}
21594
21595// --- command.mdbg.b.AbsReserve
21596// Make sure N elements fit in array. Process dies if out of memory
21597void command::b_AbsReserve(command::mdbg& parent, int n) {
21598    u32 old_max  = parent.b_max;
21599    if (n > i32(old_max)) {
21600        u32 new_max  = i32_Max(i32_Max(old_max * 2, n), 4);
21601        void *new_mem = algo_lib::malloc_ReallocMem(parent.b_elems, old_max * sizeof(algo::cstring), new_max * sizeof(algo::cstring));
21602        if (UNLIKELY(!new_mem)) {
21603            FatalErrorExit("command.tary_nomem  field:command.mdbg.b  comment:'out of memory'");
21604        }
21605        parent.b_elems = (algo::cstring*)new_mem;
21606        parent.b_max = new_max;
21607    }
21608}
21609
21610// --- command.mdbg.b.Setary
21611// Copy contents of RHS to PARENT.
21612void command::b_Setary(command::mdbg& parent, command::mdbg &rhs) {
21613    b_RemoveAll(parent);
21614    int nnew = rhs.b_n;
21615    b_Reserve(parent, nnew); // reserve space
21616    for (int i = 0; i < nnew; i++) { // copy elements over
21617        new (parent.b_elems + i) algo::cstring(b_qFind(rhs, i));
21618        parent.b_n = i + 1;
21619    }
21620}
21621
21622// --- command.mdbg.b.Setary2
21623// Copy specified array into b, discarding previous contents.
21624// If the RHS argument aliases the array (refers to the same memory), throw exception.
21625void command::b_Setary(command::mdbg& parent, const algo::aryptr<algo::cstring> &rhs) {
21626    b_RemoveAll(parent);
21627    b_Addary(parent, rhs);
21628}
21629
21630// --- command.mdbg.b.AllocNVal
21631// Reserve space. Insert N elements at the end of the array, return pointer to array
21632algo::aryptr<algo::cstring> command::b_AllocNVal(command::mdbg& parent, int n_elems, const algo::cstring& val) {
21633    b_Reserve(parent, n_elems);
21634    int old_n  = parent.b_n;
21635    int new_n = old_n + n_elems;
21636    algo::cstring *elems = parent.b_elems;
21637    for (int i = old_n; i < new_n; i++) {
21638        new (elems + i) algo::cstring(val);
21639    }
21640    parent.b_n = new_n;
21641    return algo::aryptr<algo::cstring>(elems + old_n, n_elems);
21642}
21643
21644// --- command.mdbg.b.ReadStrptrMaybe
21645// A single element is read from input string and appended to the array.
21646// If the string contains an error, the array is untouched.
21647// Function returns success value.
21648bool command::b_ReadStrptrMaybe(command::mdbg& parent, algo::strptr in_str) {
21649    bool retval = true;
21650    algo::cstring &elem = b_Alloc(parent);
21651    retval = algo::cstring_ReadStrptrMaybe(elem, in_str);
21652    if (!retval) {
21653        b_RemoveLast(parent);
21654    }
21655    return retval;
21656}
21657
21658// --- command.mdbg..ReadFieldMaybe
21659bool command::mdbg_ReadFieldMaybe(command::mdbg& parent, algo::strptr field, algo::strptr strval) {
21660    bool retval = true;
21661    command::FieldId field_id;
21662    (void)value_SetStrptrMaybe(field_id,algo::Pathcomp(field, ".LL"));
21663    switch(field_id) {
21664        case command_FieldId_target: {
21665            retval = algo::Smallstr16_ReadStrptrMaybe(parent.target, strval);
21666            break;
21667        }
21668        case command_FieldId_in: {
21669            retval = algo::cstring_ReadStrptrMaybe(parent.in, strval);
21670            break;
21671        }
21672        case command_FieldId_args: {
21673            retval = args_ReadStrptrMaybe(parent, strval);
21674            break;
21675        }
21676        case command_FieldId_cfg: {
21677            retval = algo::Smallstr50_ReadStrptrMaybe(parent.cfg, strval);
21678            break;
21679        }
21680        case command_FieldId_disas: {
21681            retval = bool_ReadStrptrMaybe(parent.disas, strval);
21682            break;
21683        }
21684        case command_FieldId_attach: {
21685            retval = bool_ReadStrptrMaybe(parent.attach, strval);
21686            break;
21687        }
21688        case command_FieldId_b: {
21689            retval = b_ReadStrptrMaybe(parent, strval);
21690            break;
21691        }
21692        case command_FieldId_catchthrow: {
21693            retval = bool_ReadStrptrMaybe(parent.catchthrow, strval);
21694            break;
21695        }
21696        case command_FieldId_tui: {
21697            retval = bool_ReadStrptrMaybe(parent.tui, strval);
21698            break;
21699        }
21700        case command_FieldId_bcmd: {
21701            retval = algo::cstring_ReadStrptrMaybe(parent.bcmd, strval);
21702            break;
21703        }
21704        case command_FieldId_emacs: {
21705            retval = bool_ReadStrptrMaybe(parent.emacs, strval);
21706            break;
21707        }
21708        case command_FieldId_manywin: {
21709            retval = bool_ReadStrptrMaybe(parent.manywin, strval);
21710            break;
21711        }
21712        case command_FieldId_follow_child: {
21713            retval = bool_ReadStrptrMaybe(parent.follow_child, strval);
21714            break;
21715        }
21716        case command_FieldId_py: {
21717            retval = bool_ReadStrptrMaybe(parent.py, strval);
21718            break;
21719        }
21720        case command_FieldId_dry_run: {
21721            retval = bool_ReadStrptrMaybe(parent.dry_run, strval);
21722            break;
21723        }
21724        default: break;
21725    }
21726    if (!retval) {
21727        algo_lib::AppendErrtext("attr",field);
21728    }
21729    return retval;
21730}
21731
21732// --- command.mdbg..ReadTupleMaybe
21733// Read fields of command::mdbg from attributes of ascii tuple TUPLE
21734bool command::mdbg_ReadTupleMaybe(command::mdbg &parent, algo::Tuple &tuple) {
21735    bool retval = true;
21736    int anon_idx = 0;
21737    ind_beg(algo::Tuple_attrs_curs,attr,tuple) {
21738        if (ch_N(attr.name) == 0) {
21739            attr.name = mdbg_GetAnon(parent, anon_idx++);
21740        }
21741        retval = mdbg_ReadFieldMaybe(parent, attr.name, attr.value);
21742        if (!retval) {
21743            break;
21744        }
21745    }ind_end;
21746    return retval;
21747}
21748
21749// --- command.mdbg..Init
21750// Set all fields to initial values.
21751void command::mdbg_Init(command::mdbg& parent) {
21752    parent.in = algo::strptr("data");
21753    parent.args_elems 	= 0; // (command.mdbg.args)
21754    parent.args_n     	= 0; // (command.mdbg.args)
21755    parent.args_max   	= 0; // (command.mdbg.args)
21756    parent.cfg = algo::strptr("debug");
21757    parent.disas = bool(false);
21758    parent.attach = bool(false);
21759    parent.b_elems 	= 0; // (command.mdbg.b)
21760    parent.b_n     	= 0; // (command.mdbg.b)
21761    parent.b_max   	= 0; // (command.mdbg.b)
21762    parent.catchthrow = bool(true);
21763    parent.tui = bool(false);
21764    parent.bcmd = algo::strptr("");
21765    parent.emacs = bool(true);
21766    parent.manywin = bool(false);
21767    parent.follow_child = bool(false);
21768    parent.py = bool(false);
21769    parent.dry_run = bool(false);
21770}
21771
21772// --- command.mdbg..Uninit
21773void command::mdbg_Uninit(command::mdbg& parent) {
21774    command::mdbg &row = parent; (void)row;
21775
21776    // command.mdbg.b.Uninit (Tary)  //Set breakpoint, e.g. 'a.cpp:123 if cond1', 'func#3'
21777    // remove all elements from command.mdbg.b
21778    b_RemoveAll(parent);
21779    // free memory for Tary command.mdbg.b
21780    algo_lib::malloc_FreeMem(parent.b_elems, sizeof(algo::cstring)*parent.b_max); // (command.mdbg.b)
21781
21782    // command.mdbg.args.Uninit (Tary)  //Additional module args
21783    // remove all elements from command.mdbg.args
21784    args_RemoveAll(parent);
21785    // free memory for Tary command.mdbg.args
21786    algo_lib::malloc_FreeMem(parent.args_elems, sizeof(algo::cstring)*parent.args_max); // (command.mdbg.args)
21787}
21788
21789// --- command.mdbg..ToCmdline
21790// Convenience function that returns a full command line
21791// Assume command is in a directory called bin
21792tempstr command::mdbg_ToCmdline(command::mdbg& row) {
21793    tempstr ret;
21794    ret << "bin/mdbg ";
21795    mdbg_PrintArgv(row, ret);
21796    // inherit less intense verbose, debug options
21797    for (int i = 1; i < algo_lib::_db.cmdline.verbose; i++) {
21798        ret << " -verbose";
21799    }
21800    for (int i = 1; i < algo_lib::_db.cmdline.debug; i++) {
21801        ret << " -debug";
21802    }
21803    return ret;
21804}
21805
21806// --- command.mdbg..PrintArgv
21807// print string representation of ROW to string STR
21808// cfmt:command.mdbg.Argv  printfmt:Auto
21809void command::mdbg_PrintArgv(command::mdbg& row, algo::cstring& str) {
21810    algo::tempstr temp;
21811    (void)temp;
21812    (void)str;
21813    ch_RemoveAll(temp);
21814    Smallstr16_Print(row.target, temp);
21815    str << " -target:";
21816    strptr_PrintBash(temp,str);
21817    if (!(row.in == "data")) {
21818        ch_RemoveAll(temp);
21819        cstring_Print(row.in, temp);
21820        str << " -in:";
21821        strptr_PrintBash(temp,str);
21822    }
21823    ind_beg(mdbg_args_curs,value,row) {
21824        ch_RemoveAll(temp);
21825        cstring_Print(value, temp);
21826        str << " -args:";
21827        strptr_PrintBash(temp,str);
21828    }ind_end;
21829    if (!(row.cfg == "debug")) {
21830        ch_RemoveAll(temp);
21831        Smallstr50_Print(row.cfg, temp);
21832        str << " -cfg:";
21833        strptr_PrintBash(temp,str);
21834    }
21835    if (!(row.disas == false)) {
21836        ch_RemoveAll(temp);
21837        bool_Print(row.disas, temp);
21838        str << " -disas:";
21839        strptr_PrintBash(temp,str);
21840    }
21841    if (!(row.attach == false)) {
21842        ch_RemoveAll(temp);
21843        bool_Print(row.attach, temp);
21844        str << " -attach:";
21845        strptr_PrintBash(temp,str);
21846    }
21847    ind_beg(mdbg_b_curs,value,row) {
21848        ch_RemoveAll(temp);
21849        cstring_Print(value, temp);
21850        str << " -b:";
21851        strptr_PrintBash(temp,str);
21852    }ind_end;
21853    if (!(row.catchthrow == true)) {
21854        ch_RemoveAll(temp);
21855        bool_Print(row.catchthrow, temp);
21856        str << " -catchthrow:";
21857        strptr_PrintBash(temp,str);
21858    }
21859    if (!(row.tui == false)) {
21860        ch_RemoveAll(temp);
21861        bool_Print(row.tui, temp);
21862        str << " -tui:";
21863        strptr_PrintBash(temp,str);
21864    }
21865    if (!(row.bcmd == "")) {
21866        ch_RemoveAll(temp);
21867        cstring_Print(row.bcmd, temp);
21868        str << " -bcmd:";
21869        strptr_PrintBash(temp,str);
21870    }
21871    if (!(row.emacs == true)) {
21872        ch_RemoveAll(temp);
21873        bool_Print(row.emacs, temp);
21874        str << " -emacs:";
21875        strptr_PrintBash(temp,str);
21876    }
21877    if (!(row.manywin == false)) {
21878        ch_RemoveAll(temp);
21879        bool_Print(row.manywin, temp);
21880        str << " -manywin:";
21881        strptr_PrintBash(temp,str);
21882    }
21883    if (!(row.follow_child == false)) {
21884        ch_RemoveAll(temp);
21885        bool_Print(row.follow_child, temp);
21886        str << " -follow_child:";
21887        strptr_PrintBash(temp,str);
21888    }
21889    if (!(row.py == false)) {
21890        ch_RemoveAll(temp);
21891        bool_Print(row.py, temp);
21892        str << " -py:";
21893        strptr_PrintBash(temp,str);
21894    }
21895    if (!(row.dry_run == false)) {
21896        ch_RemoveAll(temp);
21897        bool_Print(row.dry_run, temp);
21898        str << " -dry_run:";
21899        strptr_PrintBash(temp,str);
21900    }
21901}
21902
21903// --- command.mdbg..GetAnon
21904algo::strptr command::mdbg_GetAnon(command::mdbg &parent, i32 idx) {
21905    (void)parent;//only to avoid -Wunused-parameter
21906    switch(idx) {
21907        case(0): return strptr("target", 6);
21908        default: return strptr("args", 4);
21909    }
21910}
21911
21912// --- command.mdbg..NArgs
21913// Used with command lines
21914// Return # of command-line arguments that must follow this argument
21915// If FIELD is invalid, return -1
21916i32 command::mdbg_NArgs(command::FieldId field, algo::strptr& out_dflt, bool* out_anon) {
21917    i32 retval = 1;
21918    switch (field) {
21919        case command_FieldId_target: { // $comment
21920            *out_anon = true;
21921        } break;
21922        case command_FieldId_in: { // $comment
21923            *out_anon = false;
21924        } break;
21925        case command_FieldId_args: { // $comment
21926            *out_anon = true;
21927        } break;
21928        case command_FieldId_cfg: { // $comment
21929            *out_anon = false;
21930        } break;
21931        case command_FieldId_disas: { // $comment
21932            *out_anon = false;
21933            retval=0;
21934            out_dflt="Y";
21935        } break;
21936        case command_FieldId_attach: { // bool: no argument required but value may be specified as disas:Y
21937            *out_anon = false;
21938            retval=0;
21939            out_dflt="Y";
21940        } break;
21941        case command_FieldId_b: { // bool: no argument required but value may be specified as attach:Y
21942            *out_anon = false;
21943        } break;
21944        case command_FieldId_catchthrow: { // bool: no argument required but value may be specified as attach:Y
21945            *out_anon = false;
21946            retval=0;
21947            out_dflt="Y";
21948        } break;
21949        case command_FieldId_tui: { // bool: no argument required but value may be specified as catchthrow:Y
21950            *out_anon = false;
21951            retval=0;
21952            out_dflt="Y";
21953        } break;
21954        case command_FieldId_bcmd: { // bool: no argument required but value may be specified as tui:Y
21955            *out_anon = false;
21956        } break;
21957        case command_FieldId_emacs: { // bool: no argument required but value may be specified as tui:Y
21958            *out_anon = false;
21959            retval=0;
21960            out_dflt="Y";
21961        } break;
21962        case command_FieldId_manywin: { // bool: no argument required but value may be specified as emacs:Y
21963            *out_anon = false;
21964            retval=0;
21965            out_dflt="Y";
21966        } break;
21967        case command_FieldId_follow_child: { // bool: no argument required but value may be specified as manywin:Y
21968            *out_anon = false;
21969            retval=0;
21970            out_dflt="Y";
21971        } break;
21972        case command_FieldId_py: { // bool: no argument required but value may be specified as follow_child:Y
21973            *out_anon = false;
21974            retval=0;
21975            out_dflt="Y";
21976        } break;
21977        case command_FieldId_dry_run: { // bool: no argument required but value may be specified as py:Y
21978            *out_anon = false;
21979            retval=0;
21980            out_dflt="Y";
21981        } break;
21982        default:
21983        retval=-1; // unrecognized
21984    }
21985    return retval;
21986}
21987
21988// --- command.mdbg_proc.mdbg.Start
21989// Start subprocess
21990// If subprocess already running, do nothing. Otherwise, start it
21991int command::mdbg_Start(command::mdbg_proc& parent) {
21992    int retval = 0;
21993    if (parent.pid == 0) {
21994        verblog(mdbg_ToCmdline(parent)); // maybe print command
21995#ifdef WIN32
21996        algo_lib::ResolveExecFname(parent.path);
21997        tempstr cmdline(mdbg_ToCmdline(parent));
21998        parent.pid = dospawn(Zeroterm(parent.path),Zeroterm(cmdline),parent.timeout,parent.fstdin,parent.fstdout,parent.fstderr);
21999#else
22000        parent.pid = fork();
22001        if (parent.pid == 0) { // child
22002            algo_lib::DieWithParent();
22003            if (parent.timeout > 0) {
22004                alarm(parent.timeout);
22005            }
22006            if (retval==0) retval=algo_lib::ApplyRedirect(parent.fstdin , 0);
22007            if (retval==0) retval=algo_lib::ApplyRedirect(parent.fstdout, 1);
22008            if (retval==0) retval=algo_lib::ApplyRedirect(parent.fstderr, 2);
22009            if (retval==0) retval= mdbg_Execv(parent);
22010            if (retval != 0) { // if start fails, print error
22011                int err=errno;
22012                prerr("command.mdbg_execv"
22013                <<Keyval("errno",err)
22014                <<Keyval("errstr",strerror(err))
22015                <<Keyval("comment","Execv failed"));
22016            }
22017            _exit(127); // if failed to start, exit anyway
22018        } else if (parent.pid == -1) {
22019            retval = errno; // failed to fork
22020        }
22021#endif
22022    }
22023    parent.status = parent.pid > 0 ? 0 : -1; // if didn't start, set error status
22024    return retval;
22025}
22026
22027// --- command.mdbg_proc.mdbg.StartRead
22028// Start subprocess & Read output
22029algo::Fildes command::mdbg_StartRead(command::mdbg_proc& parent, algo_lib::FFildes &read) {
22030    int pipefd[2];
22031    int rc=pipe(pipefd);
22032    (void)rc;
22033    read.fd.value = pipefd[0];
22034    parent.fstdout  << ">&" << pipefd[1];
22035    mdbg_Start(parent);
22036    (void)close(pipefd[1]);
22037    return read.fd;
22038}
22039
22040// --- command.mdbg_proc.mdbg.Kill
22041// Kill subprocess and wait
22042void command::mdbg_Kill(command::mdbg_proc& parent) {
22043    if (parent.pid != 0) {
22044        kill(parent.pid,9);
22045        mdbg_Wait(parent);
22046    }
22047}
22048
22049// --- command.mdbg_proc.mdbg.Wait
22050// Wait for subprocess to return
22051void command::mdbg_Wait(command::mdbg_proc& parent) {
22052    if (parent.pid > 0) {
22053        int wait_flags = 0;
22054        int wait_status = 0;
22055        int rc = -1;
22056        do {
22057            // really wait for subprocess to exit
22058            rc = waitpid(parent.pid,&wait_status,wait_flags);
22059        } while (rc==-1 && errno==EINTR);
22060        if (rc == parent.pid) {
22061            parent.status = wait_status;
22062            parent.pid = 0;
22063        }
22064    }
22065}
22066
22067// --- command.mdbg_proc.mdbg.Exec
22068// Start + Wait
22069// Execute subprocess and return exit code
22070int command::mdbg_Exec(command::mdbg_proc& parent) {
22071    mdbg_Start(parent);
22072    mdbg_Wait(parent);
22073    return parent.status;
22074}
22075
22076// --- command.mdbg_proc.mdbg.ExecX
22077// Start + Wait, throw exception on error
22078// Execute subprocess; throw human-readable exception on error
22079void command::mdbg_ExecX(command::mdbg_proc& parent) {
22080    int rc = mdbg_Exec(parent);
22081    vrfy(rc==0, tempstr() << "algo_lib.exec" << Keyval("cmd",mdbg_ToCmdline(parent))
22082    << Keyval("comment",algo::DescribeWaitStatus(parent.status)));
22083}
22084
22085// --- command.mdbg_proc.mdbg.Execv
22086// Call execv()
22087// Call execv with specified parameters
22088int command::mdbg_Execv(command::mdbg_proc& parent) {
22089    int ret = 0;
22090    algo::StringAry args;
22091    mdbg_ToArgv(parent, args);
22092    char **argv = (char**)alloca((ary_N(args)+1)*sizeof(*argv));
22093    ind_beg(algo::StringAry_ary_curs,arg,args) {
22094        argv[ind_curs(arg).index] = Zeroterm(arg);
22095    }ind_end;
22096    argv[ary_N(args)] = NULL;
22097    // if parent.path is relative, search for it in PATH
22098    algo_lib::ResolveExecFname(parent.path);
22099    ret = execv(Zeroterm(parent.path),argv);
22100    return ret;
22101}
22102
22103// --- command.mdbg_proc.mdbg.ToCmdline
22104algo::tempstr command::mdbg_ToCmdline(command::mdbg_proc& parent) {
22105    algo::tempstr retval;
22106    retval << parent.path << " ";
22107    command::mdbg_PrintArgv(parent.cmd,retval);
22108    if (ch_N(parent.fstdin)) {
22109        retval << " " << parent.fstdin;
22110    }
22111    if (ch_N(parent.fstdout)) {
22112        retval << " " << parent.fstdout;
22113    }
22114    if (ch_N(parent.fstderr)) {
22115        retval << " 2" << parent.fstderr;
22116    }
22117    return retval;
22118}
22119
22120// --- command.mdbg_proc.mdbg.ToArgv
22121// Form array from the command line
22122void command::mdbg_ToArgv(command::mdbg_proc& parent, algo::StringAry& args) {
22123    ary_RemoveAll(args);
22124    ary_Alloc(args) << parent.path;
22125
22126    if (true) {
22127        cstring *arg = &ary_Alloc(args);
22128        *arg << "-target:";
22129        Smallstr16_Print(parent.cmd.target, *arg);
22130    }
22131
22132    if (parent.cmd.in != "data") {
22133        cstring *arg = &ary_Alloc(args);
22134        *arg << "-in:";
22135        cstring_Print(parent.cmd.in, *arg);
22136    }
22137    ind_beg(command::mdbg_args_curs,value,parent.cmd) {
22138        cstring *arg = &ary_Alloc(args);
22139        *arg << "-args:";
22140        cstring_Print(value, *arg);
22141    }ind_end;
22142
22143    if (parent.cmd.cfg != "debug") {
22144        cstring *arg = &ary_Alloc(args);
22145        *arg << "-cfg:";
22146        Smallstr50_Print(parent.cmd.cfg, *arg);
22147    }
22148
22149    if (parent.cmd.disas != false) {
22150        cstring *arg = &ary_Alloc(args);
22151        *arg << "-disas:";
22152        bool_Print(parent.cmd.disas, *arg);
22153    }
22154
22155    if (parent.cmd.attach != false) {
22156        cstring *arg = &ary_Alloc(args);
22157        *arg << "-attach:";
22158        bool_Print(parent.cmd.attach, *arg);
22159    }
22160    ind_beg(command::mdbg_b_curs,value,parent.cmd) {
22161        cstring *arg = &ary_Alloc(args);
22162        *arg << "-b:";
22163        cstring_Print(value, *arg);
22164    }ind_end;
22165
22166    if (parent.cmd.catchthrow != true) {
22167        cstring *arg = &ary_Alloc(args);
22168        *arg << "-catchthrow:";
22169        bool_Print(parent.cmd.catchthrow, *arg);
22170    }
22171
22172    if (parent.cmd.tui != false) {
22173        cstring *arg = &ary_Alloc(args);
22174        *arg << "-tui:";
22175        bool_Print(parent.cmd.tui, *arg);
22176    }
22177
22178    if (parent.cmd.bcmd != "") {
22179        cstring *arg = &ary_Alloc(args);
22180        *arg << "-bcmd:";
22181        cstring_Print(parent.cmd.bcmd, *arg);
22182    }
22183
22184    if (parent.cmd.emacs != true) {
22185        cstring *arg = &ary_Alloc(args);
22186        *arg << "-emacs:";
22187        bool_Print(parent.cmd.emacs, *arg);
22188    }
22189
22190    if (parent.cmd.manywin != false) {
22191        cstring *arg = &ary_Alloc(args);
22192        *arg << "-manywin:";
22193        bool_Print(parent.cmd.manywin, *arg);
22194    }
22195
22196    if (parent.cmd.follow_child != false) {
22197        cstring *arg = &ary_Alloc(args);
22198        *arg << "-follow_child:";
22199        bool_Print(parent.cmd.follow_child, *arg);
22200    }
22201
22202    if (parent.cmd.py != false) {
22203        cstring *arg = &ary_Alloc(args);
22204        *arg << "-py:";
22205        bool_Print(parent.cmd.py, *arg);
22206    }
22207
22208    if (parent.cmd.dry_run != false) {
22209        cstring *arg = &ary_Alloc(args);
22210        *arg << "-dry_run:";
22211        bool_Print(parent.cmd.dry_run, *arg);
22212    }
22213    for (int i=1; i < algo_lib::_db.cmdline.verbose; ++i) {
22214        ary_Alloc(args) << "-verbose";
22215    }
22216}
22217
22218// --- command.mdbg_proc..Uninit
22219void command::mdbg_proc_Uninit(command::mdbg_proc& parent) {
22220    command::mdbg_proc &row = parent; (void)row;
22221
22222    // command.mdbg_proc.mdbg.Uninit (Exec)  //
22223    mdbg_Kill(parent); // kill child, ensure forward progress
22224}
22225
22226// --- command.mysql2ssim..ReadFieldMaybe
22227bool command::mysql2ssim_ReadFieldMaybe(command::mysql2ssim& parent, algo::strptr field, algo::strptr strval) {
22228    bool retval = true;
22229    command::FieldId field_id;
22230    (void)value_SetStrptrMaybe(field_id,field);
22231    switch(field_id) {
22232        case command_FieldId_writessimfile: {
22233            retval = bool_ReadStrptrMaybe(parent.writessimfile, strval);
22234            break;
22235        }
22236        case command_FieldId_url: {
22237            retval = algo::cstring_ReadStrptrMaybe(parent.url, strval);
22238            break;
22239        }
22240        case command_FieldId_tables: {
22241            retval = algo::cstring_ReadStrptrMaybe(parent.tables, strval);
22242            break;
22243        }
22244        case command_FieldId_schema: {
22245            retval = bool_ReadStrptrMaybe(parent.schema, strval);
22246            break;
22247        }
22248        case command_FieldId_in: {
22249            retval = algo::cstring_ReadStrptrMaybe(parent.in, strval);
22250            break;
22251        }
22252        case command_FieldId_pretty: {
22253            retval = bool_ReadStrptrMaybe(parent.pretty, strval);
22254            break;
22255        }
22256        case command_FieldId_nologo: {
22257            retval = bool_ReadStrptrMaybe(parent.nologo, strval);
22258            break;
22259        }
22260        case command_FieldId_baddbok: {
22261            retval = bool_ReadStrptrMaybe(parent.baddbok, strval);
22262            break;
22263        }
22264        default: break;
22265    }
22266    if (!retval) {
22267        algo_lib::AppendErrtext("attr",field);
22268    }
22269    return retval;
22270}
22271
22272// --- command.mysql2ssim..ReadTupleMaybe
22273// Read fields of command::mysql2ssim from attributes of ascii tuple TUPLE
22274bool command::mysql2ssim_ReadTupleMaybe(command::mysql2ssim &parent, algo::Tuple &tuple) {
22275    bool retval = true;
22276    int anon_idx = 0;
22277    ind_beg(algo::Tuple_attrs_curs,attr,tuple) {
22278        if (ch_N(attr.name) == 0) {
22279            attr.name = mysql2ssim_GetAnon(parent, anon_idx++);
22280        }
22281        retval = mysql2ssim_ReadFieldMaybe(parent, attr.name, attr.value);
22282        if (!retval) {
22283            break;
22284        }
22285    }ind_end;
22286    return retval;
22287}
22288
22289// --- command.mysql2ssim..ToCmdline
22290// Convenience function that returns a full command line
22291// Assume command is in a directory called bin
22292tempstr command::mysql2ssim_ToCmdline(command::mysql2ssim& row) {
22293    tempstr ret;
22294    ret << "bin/mysql2ssim ";
22295    mysql2ssim_PrintArgv(row, ret);
22296    // inherit less intense verbose, debug options
22297    for (int i = 1; i < algo_lib::_db.cmdline.verbose; i++) {
22298        ret << " -verbose";
22299    }
22300    for (int i = 1; i < algo_lib::_db.cmdline.debug; i++) {
22301        ret << " -debug";
22302    }
22303    return ret;
22304}
22305
22306// --- command.mysql2ssim..PrintArgv
22307// print string representation of ROW to string STR
22308// cfmt:command.mysql2ssim.Argv  printfmt:Auto
22309void command::mysql2ssim_PrintArgv(command::mysql2ssim& row, algo::cstring& str) {
22310    algo::tempstr temp;
22311    (void)temp;
22312    (void)str;
22313    if (!(row.writessimfile == false)) {
22314        ch_RemoveAll(temp);
22315        bool_Print(row.writessimfile, temp);
22316        str << " -writessimfile:";
22317        strptr_PrintBash(temp,str);
22318    }
22319    ch_RemoveAll(temp);
22320    cstring_Print(row.url, temp);
22321    str << " -url:";
22322    strptr_PrintBash(temp,str);
22323    ch_RemoveAll(temp);
22324    cstring_Print(row.tables, temp);
22325    str << " -tables:";
22326    strptr_PrintBash(temp,str);
22327    if (!(row.schema == false)) {
22328        ch_RemoveAll(temp);
22329        bool_Print(row.schema, temp);
22330        str << " -schema:";
22331        strptr_PrintBash(temp,str);
22332    }
22333    if (!(row.in == "data")) {
22334        ch_RemoveAll(temp);
22335        cstring_Print(row.in, temp);
22336        str << " -in:";
22337        strptr_PrintBash(temp,str);
22338    }
22339    if (!(row.pretty == false)) {
22340        ch_RemoveAll(temp);
22341        bool_Print(row.pretty, temp);
22342        str << " -pretty:";
22343        strptr_PrintBash(temp,str);
22344    }
22345    if (!(row.nologo == false)) {
22346        ch_RemoveAll(temp);
22347        bool_Print(row.nologo, temp);
22348        str << " -nologo:";
22349        strptr_PrintBash(temp,str);
22350    }
22351    if (!(row.baddbok == false)) {
22352        ch_RemoveAll(temp);
22353        bool_Print(row.baddbok, temp);
22354        str << " -baddbok:";
22355        strptr_PrintBash(temp,str);
22356    }
22357}
22358
22359// --- command.mysql2ssim..GetAnon
22360algo::strptr command::mysql2ssim_GetAnon(command::mysql2ssim &parent, i32 idx) {
22361    (void)parent;//only to avoid -Wunused-parameter
22362    switch(idx) {
22363        case(0): return strptr("url", 3);
22364        case(1): return strptr("tables", 6);
22365        default: return algo::strptr();
22366    }
22367}
22368
22369// --- command.mysql2ssim..NArgs
22370// Used with command lines
22371// Return # of command-line arguments that must follow this argument
22372// If FIELD is invalid, return -1
22373i32 command::mysql2ssim_NArgs(command::FieldId field, algo::strptr& out_dflt, bool* out_anon) {
22374    i32 retval = 1;
22375    switch (field) {
22376        case command_FieldId_writessimfile: { // $comment
22377            *out_anon = false;
22378            retval=0;
22379            out_dflt="Y";
22380        } break;
22381        case command_FieldId_url: { // bool: no argument required but value may be specified as writessimfile:Y
22382            *out_anon = true;
22383        } break;
22384        case command_FieldId_tables: { // bool: no argument required but value may be specified as writessimfile:Y
22385            *out_anon = true;
22386        } break;
22387        case command_FieldId_schema: { // bool: no argument required but value may be specified as writessimfile:Y
22388            *out_anon = false;
22389            retval=0;
22390            out_dflt="Y";
22391        } break;
22392        case command_FieldId_in: { // bool: no argument required but value may be specified as schema:Y
22393            *out_anon = false;
22394        } break;
22395        case command_FieldId_pretty: { // bool: no argument required but value may be specified as schema:Y
22396            *out_anon = false;
22397            retval=0;
22398            out_dflt="Y";
22399        } break;
22400        case command_FieldId_nologo: { // bool: no argument required but value may be specified as pretty:Y
22401            *out_anon = false;
22402            retval=0;
22403            out_dflt="Y";
22404        } break;
22405        case command_FieldId_baddbok: { // bool: no argument required but value may be specified as nologo:Y
22406            *out_anon = false;
22407            retval=0;
22408            out_dflt="Y";
22409        } break;
22410        default:
22411        retval=-1; // unrecognized
22412    }
22413    return retval;
22414}
22415
22416// --- command.mysql2ssim_proc.mysql2ssim.Start
22417// Start subprocess
22418// If subprocess already running, do nothing. Otherwise, start it
22419int command::mysql2ssim_Start(command::mysql2ssim_proc& parent) {
22420    int retval = 0;
22421    if (parent.pid == 0) {
22422        verblog(mysql2ssim_ToCmdline(parent)); // maybe print command
22423#ifdef WIN32
22424        algo_lib::ResolveExecFname(parent.path);
22425        tempstr cmdline(mysql2ssim_ToCmdline(parent));
22426        parent.pid = dospawn(Zeroterm(parent.path),Zeroterm(cmdline),parent.timeout,parent.fstdin,parent.fstdout,parent.fstderr);
22427#else
22428        parent.pid = fork();
22429        if (parent.pid == 0) { // child
22430            algo_lib::DieWithParent();
22431            if (parent.timeout > 0) {
22432                alarm(parent.timeout);
22433            }
22434            if (retval==0) retval=algo_lib::ApplyRedirect(parent.fstdin , 0);
22435            if (retval==0) retval=algo_lib::ApplyRedirect(parent.fstdout, 1);
22436            if (retval==0) retval=algo_lib::ApplyRedirect(parent.fstderr, 2);
22437            if (retval==0) retval= mysql2ssim_Execv(parent);
22438            if (retval != 0) { // if start fails, print error
22439                int err=errno;
22440                prerr("command.mysql2ssim_execv"
22441                <<Keyval("errno",err)
22442                <<Keyval("errstr",strerror(err))
22443                <<Keyval("comment","Execv failed"));
22444            }
22445            _exit(127); // if failed to start, exit anyway
22446        } else if (parent.pid == -1) {
22447            retval = errno; // failed to fork
22448        }
22449#endif
22450    }
22451    parent.status = parent.pid > 0 ? 0 : -1; // if didn't start, set error status
22452    return retval;
22453}
22454
22455// --- command.mysql2ssim_proc.mysql2ssim.StartRead
22456// Start subprocess & Read output
22457algo::Fildes command::mysql2ssim_StartRead(command::mysql2ssim_proc& parent, algo_lib::FFildes &read) {
22458    int pipefd[2];
22459    int rc=pipe(pipefd);
22460    (void)rc;
22461    read.fd.value = pipefd[0];
22462    parent.fstdout  << ">&" << pipefd[1];
22463    mysql2ssim_Start(parent);
22464    (void)close(pipefd[1]);
22465    return read.fd;
22466}
22467
22468// --- command.mysql2ssim_proc.mysql2ssim.Kill
22469// Kill subprocess and wait
22470void command::mysql2ssim_Kill(command::mysql2ssim_proc& parent) {
22471    if (parent.pid != 0) {
22472        kill(parent.pid,9);
22473        mysql2ssim_Wait(parent);
22474    }
22475}
22476
22477// --- command.mysql2ssim_proc.mysql2ssim.Wait
22478// Wait for subprocess to return
22479void command::mysql2ssim_Wait(command::mysql2ssim_proc& parent) {
22480    if (parent.pid > 0) {
22481        int wait_flags = 0;
22482        int wait_status = 0;
22483        int rc = -1;
22484        do {
22485            // really wait for subprocess to exit
22486            rc = waitpid(parent.pid,&wait_status,wait_flags);
22487        } while (rc==-1 && errno==EINTR);
22488        if (rc == parent.pid) {
22489            parent.status = wait_status;
22490            parent.pid = 0;
22491        }
22492    }
22493}
22494
22495// --- command.mysql2ssim_proc.mysql2ssim.Exec
22496// Start + Wait
22497// Execute subprocess and return exit code
22498int command::mysql2ssim_Exec(command::mysql2ssim_proc& parent) {
22499    mysql2ssim_Start(parent);
22500    mysql2ssim_Wait(parent);
22501    return parent.status;
22502}
22503
22504// --- command.mysql2ssim_proc.mysql2ssim.ExecX
22505// Start + Wait, throw exception on error
22506// Execute subprocess; throw human-readable exception on error
22507void command::mysql2ssim_ExecX(command::mysql2ssim_proc& parent) {
22508    int rc = mysql2ssim_Exec(parent);
22509    vrfy(rc==0, tempstr() << "algo_lib.exec" << Keyval("cmd",mysql2ssim_ToCmdline(parent))
22510    << Keyval("comment",algo::DescribeWaitStatus(parent.status)));
22511}
22512
22513// --- command.mysql2ssim_proc.mysql2ssim.Execv
22514// Call execv()
22515// Call execv with specified parameters
22516int command::mysql2ssim_Execv(command::mysql2ssim_proc& parent) {
22517    int ret = 0;
22518    algo::StringAry args;
22519    mysql2ssim_ToArgv(parent, args);
22520    char **argv = (char**)alloca((ary_N(args)+1)*sizeof(*argv));
22521    ind_beg(algo::StringAry_ary_curs,arg,args) {
22522        argv[ind_curs(arg).index] = Zeroterm(arg);
22523    }ind_end;
22524    argv[ary_N(args)] = NULL;
22525    // if parent.path is relative, search for it in PATH
22526    algo_lib::ResolveExecFname(parent.path);
22527    ret = execv(Zeroterm(parent.path),argv);
22528    return ret;
22529}
22530
22531// --- command.mysql2ssim_proc.mysql2ssim.ToCmdline
22532algo::tempstr command::mysql2ssim_ToCmdline(command::mysql2ssim_proc& parent) {
22533    algo::tempstr retval;
22534    retval << parent.path << " ";
22535    command::mysql2ssim_PrintArgv(parent.cmd,retval);
22536    if (ch_N(parent.fstdin)) {
22537        retval << " " << parent.fstdin;
22538    }
22539    if (ch_N(parent.fstdout)) {
22540        retval << " " << parent.fstdout;
22541    }
22542    if (ch_N(parent.fstderr)) {
22543        retval << " 2" << parent.fstderr;
22544    }
22545    return retval;
22546}
22547
22548// --- command.mysql2ssim_proc.mysql2ssim.ToArgv
22549// Form array from the command line
22550void command::mysql2ssim_ToArgv(command::mysql2ssim_proc& parent, algo::StringAry& args) {
22551    ary_RemoveAll(args);
22552    ary_Alloc(args) << parent.path;
22553
22554    if (parent.cmd.writessimfile != false) {
22555        cstring *arg = &ary_Alloc(args);
22556        *arg << "-writessimfile:";
22557        bool_Print(parent.cmd.writessimfile, *arg);
22558    }
22559
22560    if (true) {
22561        cstring *arg = &ary_Alloc(args);
22562        *arg << "-url:";
22563        cstring_Print(parent.cmd.url, *arg);
22564    }
22565
22566    if (parent.cmd.tables != "") {
22567        cstring *arg = &ary_Alloc(args);
22568        *arg << "-tables:";
22569        cstring_Print(parent.cmd.tables, *arg);
22570    }
22571
22572    if (parent.cmd.schema != false) {
22573        cstring *arg = &ary_Alloc(args);
22574        *arg << "-schema:";
22575        bool_Print(parent.cmd.schema, *arg);
22576    }
22577
22578    if (parent.cmd.in != "data") {
22579        cstring *arg = &ary_Alloc(args);
22580        *arg << "-in:";
22581        cstring_Print(parent.cmd.in, *arg);
22582    }
22583
22584    if (parent.cmd.pretty != false) {
22585        cstring *arg = &ary_Alloc(args);
22586        *arg << "-pretty:";
22587        bool_Print(parent.cmd.pretty, *arg);
22588    }
22589
22590    if (parent.cmd.nologo != false) {
22591        cstring *arg = &ary_Alloc(args);
22592        *arg << "-nologo:";
22593        bool_Print(parent.cmd.nologo, *arg);
22594    }
22595
22596    if (parent.cmd.baddbok != false) {
22597        cstring *arg = &ary_Alloc(args);
22598        *arg << "-baddbok:";
22599        bool_Print(parent.cmd.baddbok, *arg);
22600    }
22601    for (int i=1; i < algo_lib::_db.cmdline.verbose; ++i) {
22602        ary_Alloc(args) << "-verbose";
22603    }
22604}
22605
22606// --- command.mysql2ssim_proc..Uninit
22607void command::mysql2ssim_proc_Uninit(command::mysql2ssim_proc& parent) {
22608    command::mysql2ssim_proc &row = parent; (void)row;
22609
22610    // command.mysql2ssim_proc.mysql2ssim.Uninit (Exec)  //
22611    mysql2ssim_Kill(parent); // kill child, ensure forward progress
22612}
22613
22614// --- command.orgfile.dedup.Print
22615// Print back to string
22616void command::dedup_Print(command::orgfile& parent, algo::cstring &out) {
22617    Regx_Print(parent.dedup, out);
22618}
22619
22620// --- command.orgfile.dedup.ReadStrptrMaybe
22621// Read Regx from string
22622// Convert string to field. Return success value
22623bool command::dedup_ReadStrptrMaybe(command::orgfile& parent, algo::strptr in) {
22624    bool retval = true;
22625    Regx_ReadSql(parent.dedup, in, true);
22626    return retval;
22627}
22628
22629// --- command.orgfile..ReadFieldMaybe
22630bool command::orgfile_ReadFieldMaybe(command::orgfile& parent, algo::strptr field, algo::strptr strval) {
22631    bool retval = true;
22632    command::FieldId field_id;
22633    (void)value_SetStrptrMaybe(field_id,field);
22634    switch(field_id) {
22635        case command_FieldId_in: {
22636            retval = algo::cstring_ReadStrptrMaybe(parent.in, strval);
22637            break;
22638        }
22639        case command_FieldId_move: {
22640            retval = algo::cstring_ReadStrptrMaybe(parent.move, strval);
22641            break;
22642        }
22643        case command_FieldId_dedup: {
22644            retval = dedup_ReadStrptrMaybe(parent, strval);
22645            break;
22646        }
22647        case command_FieldId_commit: {
22648            retval = bool_ReadStrptrMaybe(parent.commit, strval);
22649            break;
22650        }
22651        case command_FieldId_undo: {
22652            retval = bool_ReadStrptrMaybe(parent.undo, strval);
22653            break;
22654        }
22655        case command_FieldId_hash: {
22656            retval = algo::cstring_ReadStrptrMaybe(parent.hash, strval);
22657            break;
22658        }
22659        default: break;
22660    }
22661    if (!retval) {
22662        algo_lib::AppendErrtext("attr",field);
22663    }
22664    return retval;
22665}
22666
22667// --- command.orgfile..ReadTupleMaybe
22668// Read fields of command::orgfile from attributes of ascii tuple TUPLE
22669bool command::orgfile_ReadTupleMaybe(command::orgfile &parent, algo::Tuple &tuple) {
22670    bool retval = true;
22671    ind_beg(algo::Tuple_attrs_curs,attr,tuple) {
22672        retval = orgfile_ReadFieldMaybe(parent, attr.name, attr.value);
22673        if (!retval) {
22674            break;
22675        }
22676    }ind_end;
22677    return retval;
22678}
22679
22680// --- command.orgfile..Init
22681// Set all fields to initial values.
22682void command::orgfile_Init(command::orgfile& parent) {
22683    parent.in = algo::strptr("data");
22684    parent.move = algo::strptr("");
22685    Regx_ReadSql(parent.dedup, "", true);
22686    parent.commit = bool(false);
22687    parent.undo = bool(false);
22688    parent.hash = algo::strptr("sha1");
22689}
22690
22691// --- command.orgfile..ToCmdline
22692// Convenience function that returns a full command line
22693// Assume command is in a directory called bin
22694tempstr command::orgfile_ToCmdline(command::orgfile& row) {
22695    tempstr ret;
22696    ret << "bin/orgfile ";
22697    orgfile_PrintArgv(row, ret);
22698    // inherit less intense verbose, debug options
22699    for (int i = 1; i < algo_lib::_db.cmdline.verbose; i++) {
22700        ret << " -verbose";
22701    }
22702    for (int i = 1; i < algo_lib::_db.cmdline.debug; i++) {
22703        ret << " -debug";
22704    }
22705    return ret;
22706}
22707
22708// --- command.orgfile..PrintArgv
22709// print string representation of ROW to string STR
22710// cfmt:command.orgfile.Argv  printfmt:Tuple
22711void command::orgfile_PrintArgv(command::orgfile& row, algo::cstring& str) {
22712    algo::tempstr temp;
22713    (void)temp;
22714    (void)str;
22715    if (!(row.in == "data")) {
22716        ch_RemoveAll(temp);
22717        cstring_Print(row.in, temp);
22718        str << " -in:";
22719        strptr_PrintBash(temp,str);
22720    }
22721    if (!(row.move == "")) {
22722        ch_RemoveAll(temp);
22723        cstring_Print(row.move, temp);
22724        str << " -move:";
22725        strptr_PrintBash(temp,str);
22726    }
22727    if (!(row.dedup.expr == "")) {
22728        ch_RemoveAll(temp);
22729        command::dedup_Print(const_cast<command::orgfile&>(row), temp);
22730        str << " -dedup:";
22731        strptr_PrintBash(temp,str);
22732    }
22733    if (!(row.commit == false)) {
22734        ch_RemoveAll(temp);
22735        bool_Print(row.commit, temp);
22736        str << " -commit:";
22737        strptr_PrintBash(temp,str);
22738    }
22739    if (!(row.undo == false)) {
22740        ch_RemoveAll(temp);
22741        bool_Print(row.undo, temp);
22742        str << " -undo:";
22743        strptr_PrintBash(temp,str);
22744    }
22745    if (!(row.hash == "sha1")) {
22746        ch_RemoveAll(temp);
22747        cstring_Print(row.hash, temp);
22748        str << " -hash:";
22749        strptr_PrintBash(temp,str);
22750    }
22751}
22752
22753// --- command.orgfile..NArgs
22754// Used with command lines
22755// Return # of command-line arguments that must follow this argument
22756// If FIELD is invalid, return -1
22757i32 command::orgfile_NArgs(command::FieldId field, algo::strptr& out_dflt, bool* out_anon) {
22758    i32 retval = 1;
22759    switch (field) {
22760        case command_FieldId_in: { // $comment
22761            *out_anon = false;
22762        } break;
22763        case command_FieldId_move: { // $comment
22764            *out_anon = false;
22765        } break;
22766        case command_FieldId_dedup: { // $comment
22767            *out_anon = false;
22768        } break;
22769        case command_FieldId_commit: { // $comment
22770            *out_anon = false;
22771            retval=0;
22772            out_dflt="Y";
22773        } break;
22774        case command_FieldId_undo: { // bool: no argument required but value may be specified as commit:Y
22775            *out_anon = false;
22776            retval=0;
22777            out_dflt="Y";
22778        } break;
22779        case command_FieldId_hash: { // bool: no argument required but value may be specified as undo:Y
22780            *out_anon = false;
22781        } break;
22782        default:
22783        retval=-1; // unrecognized
22784    }
22785    return retval;
22786}
22787
22788// --- command.orgfile_proc.orgfile.Start
22789// Start subprocess
22790// If subprocess already running, do nothing. Otherwise, start it
22791int command::orgfile_Start(command::orgfile_proc& parent) {
22792    int retval = 0;
22793    if (parent.pid == 0) {
22794        verblog(orgfile_ToCmdline(parent)); // maybe print command
22795#ifdef WIN32
22796        algo_lib::ResolveExecFname(parent.path);
22797        tempstr cmdline(orgfile_ToCmdline(parent));
22798        parent.pid = dospawn(Zeroterm(parent.path),Zeroterm(cmdline),parent.timeout,parent.fstdin,parent.fstdout,parent.fstderr);
22799#else
22800        parent.pid = fork();
22801        if (parent.pid == 0) { // child
22802            algo_lib::DieWithParent();
22803            if (parent.timeout > 0) {
22804                alarm(parent.timeout);
22805            }
22806            if (retval==0) retval=algo_lib::ApplyRedirect(parent.fstdin , 0);
22807            if (retval==0) retval=algo_lib::ApplyRedirect(parent.fstdout, 1);
22808            if (retval==0) retval=algo_lib::ApplyRedirect(parent.fstderr, 2);
22809            if (retval==0) retval= orgfile_Execv(parent);
22810            if (retval != 0) { // if start fails, print error
22811                int err=errno;
22812                prerr("command.orgfile_execv"
22813                <<Keyval("errno",err)
22814                <<Keyval("errstr",strerror(err))
22815                <<Keyval("comment","Execv failed"));
22816            }
22817            _exit(127); // if failed to start, exit anyway
22818        } else if (parent.pid == -1) {
22819            retval = errno; // failed to fork
22820        }
22821#endif
22822    }
22823    parent.status = parent.pid > 0 ? 0 : -1; // if didn't start, set error status
22824    return retval;
22825}
22826
22827// --- command.orgfile_proc.orgfile.StartRead
22828// Start subprocess & Read output
22829algo::Fildes command::orgfile_StartRead(command::orgfile_proc& parent, algo_lib::FFildes &read) {
22830    int pipefd[2];
22831    int rc=pipe(pipefd);
22832    (void)rc;
22833    read.fd.value = pipefd[0];
22834    parent.fstdout  << ">&" << pipefd[1];
22835    orgfile_Start(parent);
22836    (void)close(pipefd[1]);
22837    return read.fd;
22838}
22839
22840// --- command.orgfile_proc.orgfile.Kill
22841// Kill subprocess and wait
22842void command::orgfile_Kill(command::orgfile_proc& parent) {
22843    if (parent.pid != 0) {
22844        kill(parent.pid,9);
22845        orgfile_Wait(parent);
22846    }
22847}
22848
22849// --- command.orgfile_proc.orgfile.Wait
22850// Wait for subprocess to return
22851void command::orgfile_Wait(command::orgfile_proc& parent) {
22852    if (parent.pid > 0) {
22853        int wait_flags = 0;
22854        int wait_status = 0;
22855        int rc = -1;
22856        do {
22857            // really wait for subprocess to exit
22858            rc = waitpid(parent.pid,&wait_status,wait_flags);
22859        } while (rc==-1 && errno==EINTR);
22860        if (rc == parent.pid) {
22861            parent.status = wait_status;
22862            parent.pid = 0;
22863        }
22864    }
22865}
22866
22867// --- command.orgfile_proc.orgfile.Exec
22868// Start + Wait
22869// Execute subprocess and return exit code
22870int command::orgfile_Exec(command::orgfile_proc& parent) {
22871    orgfile_Start(parent);
22872    orgfile_Wait(parent);
22873    return parent.status;
22874}
22875
22876// --- command.orgfile_proc.orgfile.ExecX
22877// Start + Wait, throw exception on error
22878// Execute subprocess; throw human-readable exception on error
22879void command::orgfile_ExecX(command::orgfile_proc& parent) {
22880    int rc = orgfile_Exec(parent);
22881    vrfy(rc==0, tempstr() << "algo_lib.exec" << Keyval("cmd",orgfile_ToCmdline(parent))
22882    << Keyval("comment",algo::DescribeWaitStatus(parent.status)));
22883}
22884
22885// --- command.orgfile_proc.orgfile.Execv
22886// Call execv()
22887// Call execv with specified parameters
22888int command::orgfile_Execv(command::orgfile_proc& parent) {
22889    int ret = 0;
22890    algo::StringAry args;
22891    orgfile_ToArgv(parent, args);
22892    char **argv = (char**)alloca((ary_N(args)+1)*sizeof(*argv));
22893    ind_beg(algo::StringAry_ary_curs,arg,args) {
22894        argv[ind_curs(arg).index] = Zeroterm(arg);
22895    }ind_end;
22896    argv[ary_N(args)] = NULL;
22897    // if parent.path is relative, search for it in PATH
22898    algo_lib::ResolveExecFname(parent.path);
22899    ret = execv(Zeroterm(parent.path),argv);
22900    return ret;
22901}
22902
22903// --- command.orgfile_proc.orgfile.ToCmdline
22904algo::tempstr command::orgfile_ToCmdline(command::orgfile_proc& parent) {
22905    algo::tempstr retval;
22906    retval << parent.path << " ";
22907    command::orgfile_PrintArgv(parent.cmd,retval);
22908    if (ch_N(parent.fstdin)) {
22909        retval << " " << parent.fstdin;
22910    }
22911    if (ch_N(parent.fstdout)) {
22912        retval << " " << parent.fstdout;
22913    }
22914    if (ch_N(parent.fstderr)) {
22915        retval << " 2" << parent.fstderr;
22916    }
22917    return retval;
22918}
22919
22920// --- command.orgfile_proc.orgfile.ToArgv
22921// Form array from the command line
22922void command::orgfile_ToArgv(command::orgfile_proc& parent, algo::StringAry& args) {
22923    ary_RemoveAll(args);
22924    ary_Alloc(args) << parent.path;
22925
22926    if (parent.cmd.in != "data") {
22927        cstring *arg = &ary_Alloc(args);
22928        *arg << "-in:";
22929        cstring_Print(parent.cmd.in, *arg);
22930    }
22931
22932    if (parent.cmd.move != "") {
22933        cstring *arg = &ary_Alloc(args);
22934        *arg << "-move:";
22935        cstring_Print(parent.cmd.move, *arg);
22936    }
22937
22938    if (parent.cmd.dedup.expr != "") {
22939        cstring *arg = &ary_Alloc(args);
22940        *arg << "-dedup:";
22941        command::dedup_Print(parent.cmd, *arg);
22942    }
22943
22944    if (parent.cmd.commit != false) {
22945        cstring *arg = &ary_Alloc(args);
22946        *arg << "-commit:";
22947        bool_Print(parent.cmd.commit, *arg);
22948    }
22949
22950    if (parent.cmd.undo != false) {
22951        cstring *arg = &ary_Alloc(args);
22952        *arg << "-undo:";
22953        bool_Print(parent.cmd.undo, *arg);
22954    }
22955
22956    if (parent.cmd.hash != "sha1") {
22957        cstring *arg = &ary_Alloc(args);
22958        *arg << "-hash:";
22959        cstring_Print(parent.cmd.hash, *arg);
22960    }
22961    for (int i=1; i < algo_lib::_db.cmdline.verbose; ++i) {
22962        ary_Alloc(args) << "-verbose";
22963    }
22964}
22965
22966// --- command.orgfile_proc..Uninit
22967void command::orgfile_proc_Uninit(command::orgfile_proc& parent) {
22968    command::orgfile_proc &row = parent; (void)row;
22969
22970    // command.orgfile_proc.orgfile.Uninit (Exec)  //
22971    orgfile_Kill(parent); // kill child, ensure forward progress
22972}
22973
22974// --- command.samp_regx.style.ToCstr
22975// Convert numeric value of field to one of predefined string constants.
22976// If string is found, return a static C string. Otherwise, return NULL.
22977const char* command::style_ToCstr(const command::samp_regx& parent) {
22978    const char *ret = NULL;
22979    switch(style_GetEnum(parent)) {
22980        case command_samp_regx_style_acr   : ret = "acr";  break;
22981        case command_samp_regx_style_shell : ret = "shell";  break;
22982        case command_samp_regx_style_classic: ret = "classic";  break;
22983        case command_samp_regx_style_literal: ret = "literal";  break;
22984    }
22985    return ret;
22986}
22987
22988// --- command.samp_regx.style.Print
22989// Convert style to a string. First, attempt conversion to a known string.
22990// If no string matches, print style as a numeric value.
22991void command::style_Print(const command::samp_regx& parent, algo::cstring &lhs) {
22992    const char *strval = style_ToCstr(parent);
22993    if (strval) {
22994        lhs << strval;
22995    } else {
22996        lhs << parent.style;
22997    }
22998}
22999
23000// --- command.samp_regx.style.SetStrptrMaybe
23001// Convert string to field.
23002// If the string is invalid, do not modify field and return false.
23003// In case of success, return true
23004bool command::style_SetStrptrMaybe(command::samp_regx& parent, algo::strptr rhs) {
23005    bool ret = false;
23006    switch (elems_N(rhs)) {
23007        case 3: {
23008            switch (u64(algo::ReadLE16(rhs.elems))|(u64(rhs[2])<<16)) {
23009                case LE_STR3('a','c','r'): {
23010                    style_SetEnum(parent,command_samp_regx_style_acr); ret = true; break;
23011                }
23012            }
23013            break;
23014        }
23015        case 5: {
23016            switch (u64(algo::ReadLE32(rhs.elems))|(u64(rhs[4])<<32)) {
23017                case LE_STR5('s','h','e','l','l'): {
23018                    style_SetEnum(parent,command_samp_regx_style_shell); ret = true; break;
23019                }
23020            }
23021            break;
23022        }
23023        case 7: {
23024            switch (u64(algo::ReadLE32(rhs.elems))|(u64(algo::ReadLE16(rhs.elems+4))<<32)|(u64(rhs[6])<<48)) {
23025                case LE_STR7('c','l','a','s','s','i','c'): {
23026                    style_SetEnum(parent,command_samp_regx_style_classic); ret = true; break;
23027                }
23028                case LE_STR7('l','i','t','e','r','a','l'): {
23029                    style_SetEnum(parent,command_samp_regx_style_literal); ret = true; break;
23030                }
23031            }
23032            break;
23033        }
23034    }
23035    return ret;
23036}
23037
23038// --- command.samp_regx.style.SetStrptr
23039// Convert string to field.
23040// If the string is invalid, set numeric value to DFLT
23041void command::style_SetStrptr(command::samp_regx& parent, algo::strptr rhs, command_samp_regx_style_Enum dflt) {
23042    if (!style_SetStrptrMaybe(parent,rhs)) style_SetEnum(parent,dflt);
23043}
23044
23045// --- command.samp_regx.style.ReadStrptrMaybe
23046// Convert string to field. Return success value
23047bool command::style_ReadStrptrMaybe(command::samp_regx& parent, algo::strptr rhs) {
23048    bool retval = false;
23049    retval = style_SetStrptrMaybe(parent,rhs); // try symbol conversion
23050    if (!retval) { // didn't work? try reading as underlying type
23051        retval = u8_ReadStrptrMaybe(parent.style,rhs);
23052    }
23053    return retval;
23054}
23055
23056// --- command.samp_regx..ReadFieldMaybe
23057bool command::samp_regx_ReadFieldMaybe(command::samp_regx& parent, algo::strptr field, algo::strptr strval) {
23058    bool retval = true;
23059    command::FieldId field_id;
23060    (void)value_SetStrptrMaybe(field_id,field);
23061    switch(field_id) {
23062        case command_FieldId_in: {
23063            retval = algo::cstring_ReadStrptrMaybe(parent.in, strval);
23064            break;
23065        }
23066        case command_FieldId_expr: {
23067            retval = algo::cstring_ReadStrptrMaybe(parent.expr, strval);
23068            break;
23069        }
23070        case command_FieldId_style: {
23071            retval = style_ReadStrptrMaybe(parent, strval);
23072            break;
23073        }
23074        case command_FieldId_match: {
23075            retval = bool_ReadStrptrMaybe(parent.match, strval);
23076            break;
23077        }
23078        case command_FieldId_string: {
23079            retval = algo::cstring_ReadStrptrMaybe(parent.string, strval);
23080            break;
23081        }
23082        case command_FieldId_show: {
23083            retval = bool_ReadStrptrMaybe(parent.show, strval);
23084            break;
23085        }
23086        default: break;
23087    }
23088    if (!retval) {
23089        algo_lib::AppendErrtext("attr",field);
23090    }
23091    return retval;
23092}
23093
23094// --- command.samp_regx..ReadTupleMaybe
23095// Read fields of command::samp_regx from attributes of ascii tuple TUPLE
23096bool command::samp_regx_ReadTupleMaybe(command::samp_regx &parent, algo::Tuple &tuple) {
23097    bool retval = true;
23098    int anon_idx = 0;
23099    ind_beg(algo::Tuple_attrs_curs,attr,tuple) {
23100        if (ch_N(attr.name) == 0) {
23101            attr.name = samp_regx_GetAnon(parent, anon_idx++);
23102        }
23103        retval = samp_regx_ReadFieldMaybe(parent, attr.name, attr.value);
23104        if (!retval) {
23105            break;
23106        }
23107    }ind_end;
23108    return retval;
23109}
23110
23111// --- command.samp_regx..ToCmdline
23112// Convenience function that returns a full command line
23113// Assume command is in a directory called bin
23114tempstr command::samp_regx_ToCmdline(command::samp_regx& row) {
23115    tempstr ret;
23116    ret << "bin/samp_regx ";
23117    samp_regx_PrintArgv(row, ret);
23118    // inherit less intense verbose, debug options
23119    for (int i = 1; i < algo_lib::_db.cmdline.verbose; i++) {
23120        ret << " -verbose";
23121    }
23122    for (int i = 1; i < algo_lib::_db.cmdline.debug; i++) {
23123        ret << " -debug";
23124    }
23125    return ret;
23126}
23127
23128// --- command.samp_regx..PrintArgv
23129// print string representation of ROW to string STR
23130// cfmt:command.samp_regx.Argv  printfmt:Tuple
23131void command::samp_regx_PrintArgv(command::samp_regx& row, algo::cstring& str) {
23132    algo::tempstr temp;
23133    (void)temp;
23134    (void)str;
23135    if (!(row.in == "data")) {
23136        ch_RemoveAll(temp);
23137        cstring_Print(row.in, temp);
23138        str << " -in:";
23139        strptr_PrintBash(temp,str);
23140    }
23141    ch_RemoveAll(temp);
23142    cstring_Print(row.expr, temp);
23143    str << " -expr:";
23144    strptr_PrintBash(temp,str);
23145    if (!(row.style == 0)) {
23146        ch_RemoveAll(temp);
23147        command::style_Print(const_cast<command::samp_regx&>(row), temp);
23148        str << " -style:";
23149        strptr_PrintBash(temp,str);
23150    }
23151    if (!(row.match == false)) {
23152        ch_RemoveAll(temp);
23153        bool_Print(row.match, temp);
23154        str << " -match:";
23155        strptr_PrintBash(temp,str);
23156    }
23157    ch_RemoveAll(temp);
23158    cstring_Print(row.string, temp);
23159    str << " -string:";
23160    strptr_PrintBash(temp,str);
23161    if (!(row.show == false)) {
23162        ch_RemoveAll(temp);
23163        bool_Print(row.show, temp);
23164        str << " -show:";
23165        strptr_PrintBash(temp,str);
23166    }
23167}
23168
23169// --- command.samp_regx..GetAnon
23170algo::strptr command::samp_regx_GetAnon(command::samp_regx &parent, i32 idx) {
23171    (void)parent;//only to avoid -Wunused-parameter
23172    switch(idx) {
23173        case(0): return strptr("expr", 4);
23174        case(1): return strptr("string", 6);
23175        default: return algo::strptr();
23176    }
23177}
23178
23179// --- command.samp_regx..NArgs
23180// Used with command lines
23181// Return # of command-line arguments that must follow this argument
23182// If FIELD is invalid, return -1
23183i32 command::samp_regx_NArgs(command::FieldId field, algo::strptr& out_dflt, bool* out_anon) {
23184    i32 retval = 1;
23185    switch (field) {
23186        case command_FieldId_in: { // $comment
23187            *out_anon = false;
23188        } break;
23189        case command_FieldId_expr: { // $comment
23190            *out_anon = true;
23191        } break;
23192        case command_FieldId_style: { // $comment
23193            *out_anon = false;
23194        } break;
23195        case command_FieldId_match: { // $comment
23196            *out_anon = false;
23197            retval=0;
23198            out_dflt="Y";
23199        } break;
23200        case command_FieldId_string: { // bool: no argument required but value may be specified as match:Y
23201            *out_anon = true;
23202        } break;
23203        case command_FieldId_show: { // bool: no argument required but value may be specified as match:Y
23204            *out_anon = false;
23205            retval=0;
23206            out_dflt="Y";
23207        } break;
23208        default:
23209        retval=-1; // unrecognized
23210    }
23211    return retval;
23212}
23213
23214// --- command.samp_regx_proc.samp_regx.Start
23215// Start subprocess
23216// If subprocess already running, do nothing. Otherwise, start it
23217int command::samp_regx_Start(command::samp_regx_proc& parent) {
23218    int retval = 0;
23219    if (parent.pid == 0) {
23220        verblog(samp_regx_ToCmdline(parent)); // maybe print command
23221#ifdef WIN32
23222        algo_lib::ResolveExecFname(parent.path);
23223        tempstr cmdline(samp_regx_ToCmdline(parent));
23224        parent.pid = dospawn(Zeroterm(parent.path),Zeroterm(cmdline),parent.timeout,parent.fstdin,parent.fstdout,parent.fstderr);
23225#else
23226        parent.pid = fork();
23227        if (parent.pid == 0) { // child
23228            algo_lib::DieWithParent();
23229            if (parent.timeout > 0) {
23230                alarm(parent.timeout);
23231            }
23232            if (retval==0) retval=algo_lib::ApplyRedirect(parent.fstdin , 0);
23233            if (retval==0) retval=algo_lib::ApplyRedirect(parent.fstdout, 1);
23234            if (retval==0) retval=algo_lib::ApplyRedirect(parent.fstderr, 2);
23235            if (retval==0) retval= samp_regx_Execv(parent);
23236            if (retval != 0) { // if start fails, print error
23237                int err=errno;
23238                prerr("command.samp_regx_execv"
23239                <<Keyval("errno",err)
23240                <<Keyval("errstr",strerror(err))
23241                <<Keyval("comment","Execv failed"));
23242            }
23243            _exit(127); // if failed to start, exit anyway
23244        } else if (parent.pid == -1) {
23245            retval = errno; // failed to fork
23246        }
23247#endif
23248    }
23249    parent.status = parent.pid > 0 ? 0 : -1; // if didn't start, set error status
23250    return retval;
23251}
23252
23253// --- command.samp_regx_proc.samp_regx.StartRead
23254// Start subprocess & Read output
23255algo::Fildes command::samp_regx_StartRead(command::samp_regx_proc& parent, algo_lib::FFildes &read) {
23256    int pipefd[2];
23257    int rc=pipe(pipefd);
23258    (void)rc;
23259    read.fd.value = pipefd[0];
23260    parent.fstdout  << ">&" << pipefd[1];
23261    samp_regx_Start(parent);
23262    (void)close(pipefd[1]);
23263    return read.fd;
23264}
23265
23266// --- command.samp_regx_proc.samp_regx.Kill
23267// Kill subprocess and wait
23268void command::samp_regx_Kill(command::samp_regx_proc& parent) {
23269    if (parent.pid != 0) {
23270        kill(parent.pid,9);
23271        samp_regx_Wait(parent);
23272    }
23273}
23274
23275// --- command.samp_regx_proc.samp_regx.Wait
23276// Wait for subprocess to return
23277void command::samp_regx_Wait(command::samp_regx_proc& parent) {
23278    if (parent.pid > 0) {
23279        int wait_flags = 0;
23280        int wait_status = 0;
23281        int rc = -1;
23282        do {
23283            // really wait for subprocess to exit
23284            rc = waitpid(parent.pid,&wait_status,wait_flags);
23285        } while (rc==-1 && errno==EINTR);
23286        if (rc == parent.pid) {
23287            parent.status = wait_status;
23288            parent.pid = 0;
23289        }
23290    }
23291}
23292
23293// --- command.samp_regx_proc.samp_regx.Exec
23294// Start + Wait
23295// Execute subprocess and return exit code
23296int command::samp_regx_Exec(command::samp_regx_proc& parent) {
23297    samp_regx_Start(parent);
23298    samp_regx_Wait(parent);
23299    return parent.status;
23300}
23301
23302// --- command.samp_regx_proc.samp_regx.ExecX
23303// Start + Wait, throw exception on error
23304// Execute subprocess; throw human-readable exception on error
23305void command::samp_regx_ExecX(command::samp_regx_proc& parent) {
23306    int rc = samp_regx_Exec(parent);
23307    vrfy(rc==0, tempstr() << "algo_lib.exec" << Keyval("cmd",samp_regx_ToCmdline(parent))
23308    << Keyval("comment",algo::DescribeWaitStatus(parent.status)));
23309}
23310
23311// --- command.samp_regx_proc.samp_regx.Execv
23312// Call execv()
23313// Call execv with specified parameters
23314int command::samp_regx_Execv(command::samp_regx_proc& parent) {
23315    int ret = 0;
23316    algo::StringAry args;
23317    samp_regx_ToArgv(parent, args);
23318    char **argv = (char**)alloca((ary_N(args)+1)*sizeof(*argv));
23319    ind_beg(algo::StringAry_ary_curs,arg,args) {
23320        argv[ind_curs(arg).index] = Zeroterm(arg);
23321    }ind_end;
23322    argv[ary_N(args)] = NULL;
23323    // if parent.path is relative, search for it in PATH
23324    algo_lib::ResolveExecFname(parent.path);
23325    ret = execv(Zeroterm(parent.path),argv);
23326    return ret;
23327}
23328
23329// --- command.samp_regx_proc.samp_regx.ToCmdline
23330algo::tempstr command::samp_regx_ToCmdline(command::samp_regx_proc& parent) {
23331    algo::tempstr retval;
23332    retval << parent.path << " ";
23333    command::samp_regx_PrintArgv(parent.cmd,retval);
23334    if (ch_N(parent.fstdin)) {
23335        retval << " " << parent.fstdin;
23336    }
23337    if (ch_N(parent.fstdout)) {
23338        retval << " " << parent.fstdout;
23339    }
23340    if (ch_N(parent.fstderr)) {
23341        retval << " 2" << parent.fstderr;
23342    }
23343    return retval;
23344}
23345
23346// --- command.samp_regx_proc.samp_regx.ToArgv
23347// Form array from the command line
23348void command::samp_regx_ToArgv(command::samp_regx_proc& parent, algo::StringAry& args) {
23349    ary_RemoveAll(args);
23350    ary_Alloc(args) << parent.path;
23351
23352    if (parent.cmd.in != "data") {
23353        cstring *arg = &ary_Alloc(args);
23354        *arg << "-in:";
23355        cstring_Print(parent.cmd.in, *arg);
23356    }
23357
23358    if (true) {
23359        cstring *arg = &ary_Alloc(args);
23360        *arg << "-expr:";
23361        cstring_Print(parent.cmd.expr, *arg);
23362    }
23363
23364    if (parent.cmd.style != 0) {
23365        cstring *arg = &ary_Alloc(args);
23366        *arg << "-style:";
23367        command::style_Print(parent.cmd, *arg);
23368    }
23369
23370    if (parent.cmd.match != false) {
23371        cstring *arg = &ary_Alloc(args);
23372        *arg << "-match:";
23373        bool_Print(parent.cmd.match, *arg);
23374    }
23375
23376    if (parent.cmd.string != "") {
23377        cstring *arg = &ary_Alloc(args);
23378        *arg << "-string:";
23379        cstring_Print(parent.cmd.string, *arg);
23380    }
23381
23382    if (parent.cmd.show != false) {
23383        cstring *arg = &ary_Alloc(args);
23384        *arg << "-show:";
23385        bool_Print(parent.cmd.show, *arg);
23386    }
23387    for (int i=1; i < algo_lib::_db.cmdline.verbose; ++i) {
23388        ary_Alloc(args) << "-verbose";
23389    }
23390}
23391
23392// --- command.samp_regx_proc..Uninit
23393void command::samp_regx_proc_Uninit(command::samp_regx_proc& parent) {
23394    command::samp_regx_proc &row = parent; (void)row;
23395
23396    // command.samp_regx_proc.samp_regx.Uninit (Exec)  //
23397    samp_regx_Kill(parent); // kill child, ensure forward progress
23398}
23399
23400// --- command.samp_ws..ReadFieldMaybe
23401bool command::samp_ws_ReadFieldMaybe(command::samp_ws& parent, algo::strptr field, algo::strptr strval) {
23402    bool retval = true;
23403    command::FieldId field_id;
23404    (void)value_SetStrptrMaybe(field_id,field);
23405    switch(field_id) {
23406        case command_FieldId_in: {
23407            retval = algo::cstring_ReadStrptrMaybe(parent.in, strval);
23408            break;
23409        }
23410        default: break;
23411    }
23412    if (!retval) {
23413        algo_lib::AppendErrtext("attr",field);
23414    }
23415    return retval;
23416}
23417
23418// --- command.samp_ws..ReadTupleMaybe
23419// Read fields of command::samp_ws from attributes of ascii tuple TUPLE
23420bool command::samp_ws_ReadTupleMaybe(command::samp_ws &parent, algo::Tuple &tuple) {
23421    bool retval = true;
23422    ind_beg(algo::Tuple_attrs_curs,attr,tuple) {
23423        retval = samp_ws_ReadFieldMaybe(parent, attr.name, attr.value);
23424        if (!retval) {
23425            break;
23426        }
23427    }ind_end;
23428    return retval;
23429}
23430
23431// --- command.samp_ws..ToCmdline
23432// Convenience function that returns a full command line
23433// Assume command is in a directory called bin
23434tempstr command::samp_ws_ToCmdline(command::samp_ws& row) {
23435    tempstr ret;
23436    ret << "bin/samp_ws ";
23437    samp_ws_PrintArgv(row, ret);
23438    // inherit less intense verbose, debug options
23439    for (int i = 1; i < algo_lib::_db.cmdline.verbose; i++) {
23440        ret << " -verbose";
23441    }
23442    for (int i = 1; i < algo_lib::_db.cmdline.debug; i++) {
23443        ret << " -debug";
23444    }
23445    return ret;
23446}
23447
23448// --- command.samp_ws..PrintArgv
23449// print string representation of ROW to string STR
23450// cfmt:command.samp_ws.Argv  printfmt:Tuple
23451void command::samp_ws_PrintArgv(command::samp_ws& row, algo::cstring& str) {
23452    algo::tempstr temp;
23453    (void)temp;
23454    (void)str;
23455    if (!(row.in == "data")) {
23456        ch_RemoveAll(temp);
23457        cstring_Print(row.in, temp);
23458        str << " -in:";
23459        strptr_PrintBash(temp,str);
23460    }
23461}
23462
23463// --- command.samp_ws..NArgs
23464// Used with command lines
23465// Return # of command-line arguments that must follow this argument
23466// If FIELD is invalid, return -1
23467i32 command::samp_ws_NArgs(command::FieldId field, algo::strptr& out_dflt, bool* out_anon) {
23468    i32 retval = 1;
23469    switch (field) {
23470        case command_FieldId_in: { // $comment
23471            *out_anon = false;
23472        } break;
23473        default:
23474        retval=-1; // unrecognized
23475    }
23476    (void)out_dflt;//only to avoid -Wunused-parameter
23477    return retval;
23478}
23479
23480// --- command.samp_ws_proc.samp_ws.Start
23481// Start subprocess
23482// If subprocess already running, do nothing. Otherwise, start it
23483int command::samp_ws_Start(command::samp_ws_proc& parent) {
23484    int retval = 0;
23485    if (parent.pid == 0) {
23486        verblog(samp_ws_ToCmdline(parent)); // maybe print command
23487#ifdef WIN32
23488        algo_lib::ResolveExecFname(parent.path);
23489        tempstr cmdline(samp_ws_ToCmdline(parent));
23490        parent.pid = dospawn(Zeroterm(parent.path),Zeroterm(cmdline),parent.timeout,parent.fstdin,parent.fstdout,parent.fstderr);
23491#else
23492        parent.pid = fork();
23493        if (parent.pid == 0) { // child
23494            algo_lib::DieWithParent();
23495            if (parent.timeout > 0) {
23496                alarm(parent.timeout);
23497            }
23498            if (retval==0) retval=algo_lib::ApplyRedirect(parent.fstdin , 0);
23499            if (retval==0) retval=algo_lib::ApplyRedirect(parent.fstdout, 1);
23500            if (retval==0) retval=algo_lib::ApplyRedirect(parent.fstderr, 2);
23501            if (retval==0) retval= samp_ws_Execv(parent);
23502            if (retval != 0) { // if start fails, print error
23503                int err=errno;
23504                prerr("command.samp_ws_execv"
23505                <<Keyval("errno",err)
23506                <<Keyval("errstr",strerror(err))
23507                <<Keyval("comment","Execv failed"));
23508            }
23509            _exit(127); // if failed to start, exit anyway
23510        } else if (parent.pid == -1) {
23511            retval = errno; // failed to fork
23512        }
23513#endif
23514    }
23515    parent.status = parent.pid > 0 ? 0 : -1; // if didn't start, set error status
23516    return retval;
23517}
23518
23519// --- command.samp_ws_proc.samp_ws.StartRead
23520// Start subprocess & Read output
23521algo::Fildes command::samp_ws_StartRead(command::samp_ws_proc& parent, algo_lib::FFildes &read) {
23522    int pipefd[2];
23523    int rc=pipe(pipefd);
23524    (void)rc;
23525    read.fd.value = pipefd[0];
23526    parent.fstdout  << ">&" << pipefd[1];
23527    samp_ws_Start(parent);
23528    (void)close(pipefd[1]);
23529    return read.fd;
23530}
23531
23532// --- command.samp_ws_proc.samp_ws.Kill
23533// Kill subprocess and wait
23534void command::samp_ws_Kill(command::samp_ws_proc& parent) {
23535    if (parent.pid != 0) {
23536        kill(parent.pid,9);
23537        samp_ws_Wait(parent);
23538    }
23539}
23540
23541// --- command.samp_ws_proc.samp_ws.Wait
23542// Wait for subprocess to return
23543void command::samp_ws_Wait(command::samp_ws_proc& parent) {
23544    if (parent.pid > 0) {
23545        int wait_flags = 0;
23546        int wait_status = 0;
23547        int rc = -1;
23548        do {
23549            // really wait for subprocess to exit
23550            rc = waitpid(parent.pid,&wait_status,wait_flags);
23551        } while (rc==-1 && errno==EINTR);
23552        if (rc == parent.pid) {
23553            parent.status = wait_status;
23554            parent.pid = 0;
23555        }
23556    }
23557}
23558
23559// --- command.samp_ws_proc.samp_ws.Exec
23560// Start + Wait
23561// Execute subprocess and return exit code
23562int command::samp_ws_Exec(command::samp_ws_proc& parent) {
23563    samp_ws_Start(parent);
23564    samp_ws_Wait(parent);
23565    return parent.status;
23566}
23567
23568// --- command.samp_ws_proc.samp_ws.ExecX
23569// Start + Wait, throw exception on error
23570// Execute subprocess; throw human-readable exception on error
23571void command::samp_ws_ExecX(command::samp_ws_proc& parent) {
23572    int rc = samp_ws_Exec(parent);
23573    vrfy(rc==0, tempstr() << "algo_lib.exec" << Keyval("cmd",samp_ws_ToCmdline(parent))
23574    << Keyval("comment",algo::DescribeWaitStatus(parent.status)));
23575}
23576
23577// --- command.samp_ws_proc.samp_ws.Execv
23578// Call execv()
23579// Call execv with specified parameters
23580int command::samp_ws_Execv(command::samp_ws_proc& parent) {
23581    int ret = 0;
23582    algo::StringAry args;
23583    samp_ws_ToArgv(parent, args);
23584    char **argv = (char**)alloca((ary_N(args)+1)*sizeof(*argv));
23585    ind_beg(algo::StringAry_ary_curs,arg,args) {
23586        argv[ind_curs(arg).index] = Zeroterm(arg);
23587    }ind_end;
23588    argv[ary_N(args)] = NULL;
23589    // if parent.path is relative, search for it in PATH
23590    algo_lib::ResolveExecFname(parent.path);
23591    ret = execv(Zeroterm(parent.path),argv);
23592    return ret;
23593}
23594
23595// --- command.samp_ws_proc.samp_ws.ToCmdline
23596algo::tempstr command::samp_ws_ToCmdline(command::samp_ws_proc& parent) {
23597    algo::tempstr retval;
23598    retval << parent.path << " ";
23599    command::samp_ws_PrintArgv(parent.cmd,retval);
23600    if (ch_N(parent.fstdin)) {
23601        retval << " " << parent.fstdin;
23602    }
23603    if (ch_N(parent.fstdout)) {
23604        retval << " " << parent.fstdout;
23605    }
23606    if (ch_N(parent.fstderr)) {
23607        retval << " 2" << parent.fstderr;
23608    }
23609    return retval;
23610}
23611
23612// --- command.samp_ws_proc.samp_ws.ToArgv
23613// Form array from the command line
23614void command::samp_ws_ToArgv(command::samp_ws_proc& parent, algo::StringAry& args) {
23615    ary_RemoveAll(args);
23616    ary_Alloc(args) << parent.path;
23617
23618    if (parent.cmd.in != "data") {
23619        cstring *arg = &ary_Alloc(args);
23620        *arg << "-in:";
23621        cstring_Print(parent.cmd.in, *arg);
23622    }
23623    for (int i=1; i < algo_lib::_db.cmdline.verbose; ++i) {
23624        ary_Alloc(args) << "-verbose";
23625    }
23626}
23627
23628// --- command.samp_ws_proc..Uninit
23629void command::samp_ws_proc_Uninit(command::samp_ws_proc& parent) {
23630    command::samp_ws_proc &row = parent; (void)row;
23631
23632    // command.samp_ws_proc.samp_ws.Uninit (Exec)  //
23633    samp_ws_Kill(parent); // kill child, ensure forward progress
23634}
23635
23636// --- command.sandbox.name.Print
23637// Print back to string
23638void command::name_Print(command::sandbox& parent, algo::cstring &out) {
23639    Regx_Print(parent.name, out);
23640}
23641
23642// --- command.sandbox.name.ReadStrptrMaybe
23643// Read Regx from string
23644// Convert string to field. Return success value
23645bool command::name_ReadStrptrMaybe(command::sandbox& parent, algo::strptr in) {
23646    bool retval = true;
23647    Regx_ReadSql(parent.name, in, true);
23648    return retval;
23649}
23650
23651// --- command.sandbox.cmd.Addary
23652// Reserve space (this may move memory). Insert N element at the end.
23653// Return aryptr to newly inserted block.
23654// If the RHS argument aliases the array (refers to the same memory), exit program with fatal error.
23655algo::aryptr<algo::cstring> command::cmd_Addary(command::sandbox& parent, algo::aryptr<algo::cstring> rhs) {
23656    bool overlaps = rhs.n_elems>0 && rhs.elems >= parent.cmd_elems && rhs.elems < parent.cmd_elems + parent.cmd_max;
23657    if (UNLIKELY(overlaps)) {
23658        FatalErrorExit("command.tary_alias  field:command.sandbox.cmd  comment:'alias error: sub-array is being appended to the whole'");
23659    }
23660    int nnew = rhs.n_elems;
23661    cmd_Reserve(parent, nnew); // reserve space
23662    int at = parent.cmd_n;
23663    for (int i = 0; i < nnew; i++) {
23664        new (parent.cmd_elems + at + i) algo::cstring(rhs[i]);
23665        parent.cmd_n++;
23666    }
23667    return algo::aryptr<algo::cstring>(parent.cmd_elems + at, nnew);
23668}
23669
23670// --- command.sandbox.cmd.Alloc
23671// Reserve space. Insert element at the end
23672// The new element is initialized to a default value
23673algo::cstring& command::cmd_Alloc(command::sandbox& parent) {
23674    cmd_Reserve(parent, 1);
23675    int n  = parent.cmd_n;
23676    int at = n;
23677    algo::cstring *elems = parent.cmd_elems;
23678    new (elems + at) algo::cstring(); // construct new element, default initializer
23679    parent.cmd_n = n+1;
23680    return elems[at];
23681}
23682
23683// --- command.sandbox.cmd.AllocAt
23684// Reserve space for new element, reallocating the array if necessary
23685// Insert new element at specified index. Index must be in range or a fatal error occurs.
23686algo::cstring& command::cmd_AllocAt(command::sandbox& parent, int at) {
23687    cmd_Reserve(parent, 1);
23688    int n  = parent.cmd_n;
23689    if (UNLIKELY(u64(at) >= u64(n+1))) {
23690        FatalErrorExit("command.bad_alloc_at  field:command.sandbox.cmd  comment:'index out of range'");
23691    }
23692    algo::cstring *elems = parent.cmd_elems;
23693    memmove(elems + at + 1, elems + at, (n - at) * sizeof(algo::cstring));
23694    new (elems + at) algo::cstring(); // construct element, default initializer
23695    parent.cmd_n = n+1;
23696    return elems[at];
23697}
23698
23699// --- command.sandbox.cmd.AllocN
23700// Reserve space. Insert N elements at the end of the array, return pointer to array
23701algo::aryptr<algo::cstring> command::cmd_AllocN(command::sandbox& parent, int n_elems) {
23702    cmd_Reserve(parent, n_elems);
23703    int old_n  = parent.cmd_n;
23704    int new_n = old_n + n_elems;
23705    algo::cstring *elems = parent.cmd_elems;
23706    for (int i = old_n; i < new_n; i++) {
23707        new (elems + i) algo::cstring(); // construct new element, default initialize
23708    }
23709    parent.cmd_n = new_n;
23710    return algo::aryptr<algo::cstring>(elems + old_n, n_elems);
23711}
23712
23713// --- command.sandbox.cmd.Remove
23714// Remove item by index. If index outside of range, do nothing.
23715void command::cmd_Remove(command::sandbox& parent, u32 i) {
23716    u32 lim = parent.cmd_n;
23717    algo::cstring *elems = parent.cmd_elems;
23718    if (i < lim) {
23719        elems[i].~cstring(); // destroy element
23720        memmove(elems + i, elems + (i + 1), sizeof(algo::cstring) * (lim - (i + 1)));
23721        parent.cmd_n = lim - 1;
23722    }
23723}
23724
23725// --- command.sandbox.cmd.RemoveAll
23726void command::cmd_RemoveAll(command::sandbox& parent) {
23727    u32 n = parent.cmd_n;
23728    while (n > 0) {
23729        n -= 1;
23730        parent.cmd_elems[n].~cstring();
23731        parent.cmd_n = n;
23732    }
23733}
23734
23735// --- command.sandbox.cmd.RemoveLast
23736// Delete last element of array. Do nothing if array is empty.
23737void command::cmd_RemoveLast(command::sandbox& parent) {
23738    u64 n = parent.cmd_n;
23739    if (n > 0) {
23740        n -= 1;
23741        cmd_qFind(parent, u64(n)).~cstring();
23742        parent.cmd_n = n;
23743    }
23744}
23745
23746// --- command.sandbox.cmd.AbsReserve
23747// Make sure N elements fit in array. Process dies if out of memory
23748void command::cmd_AbsReserve(command::sandbox& parent, int n) {
23749    u32 old_max  = parent.cmd_max;
23750    if (n > i32(old_max)) {
23751        u32 new_max  = i32_Max(i32_Max(old_max * 2, n), 4);
23752        void *new_mem = algo_lib::malloc_ReallocMem(parent.cmd_elems, old_max * sizeof(algo::cstring), new_max * sizeof(algo::cstring));
23753        if (UNLIKELY(!new_mem)) {
23754            FatalErrorExit("command.tary_nomem  field:command.sandbox.cmd  comment:'out of memory'");
23755        }
23756        parent.cmd_elems = (algo::cstring*)new_mem;
23757        parent.cmd_max = new_max;
23758    }
23759}
23760
23761// --- command.sandbox.cmd.Setary
23762// Copy contents of RHS to PARENT.
23763void command::cmd_Setary(command::sandbox& parent, command::sandbox &rhs) {
23764    cmd_RemoveAll(parent);
23765    int nnew = rhs.cmd_n;
23766    cmd_Reserve(parent, nnew); // reserve space
23767    for (int i = 0; i < nnew; i++) { // copy elements over
23768        new (parent.cmd_elems + i) algo::cstring(cmd_qFind(rhs, i));
23769        parent.cmd_n = i + 1;
23770    }
23771}
23772
23773// --- command.sandbox.cmd.Setary2
23774// Copy specified array into cmd, discarding previous contents.
23775// If the RHS argument aliases the array (refers to the same memory), throw exception.
23776void command::cmd_Setary(command::sandbox& parent, const algo::aryptr<algo::cstring> &rhs) {
23777    cmd_RemoveAll(parent);
23778    cmd_Addary(parent, rhs);
23779}
23780
23781// --- command.sandbox.cmd.AllocNVal
23782// Reserve space. Insert N elements at the end of the array, return pointer to array
23783algo::aryptr<algo::cstring> command::cmd_AllocNVal(command::sandbox& parent, int n_elems, const algo::cstring& val) {
23784    cmd_Reserve(parent, n_elems);
23785    int old_n  = parent.cmd_n;
23786    int new_n = old_n + n_elems;
23787    algo::cstring *elems = parent.cmd_elems;
23788    for (int i = old_n; i < new_n; i++) {
23789        new (elems + i) algo::cstring(val);
23790    }
23791    parent.cmd_n = new_n;
23792    return algo::aryptr<algo::cstring>(elems + old_n, n_elems);
23793}
23794
23795// --- command.sandbox.cmd.ReadStrptrMaybe
23796// A single element is read from input string and appended to the array.
23797// If the string contains an error, the array is untouched.
23798// Function returns success value.
23799bool command::cmd_ReadStrptrMaybe(command::sandbox& parent, algo::strptr in_str) {
23800    bool retval = true;
23801    algo::cstring &elem = cmd_Alloc(parent);
23802    retval = algo::cstring_ReadStrptrMaybe(elem, in_str);
23803    if (!retval) {
23804        cmd_RemoveLast(parent);
23805    }
23806    return retval;
23807}
23808
23809// --- command.sandbox.files.Addary
23810// Reserve space (this may move memory). Insert N element at the end.
23811// Return aryptr to newly inserted block.
23812// If the RHS argument aliases the array (refers to the same memory), exit program with fatal error.
23813algo::aryptr<algo::cstring> command::files_Addary(command::sandbox& parent, algo::aryptr<algo::cstring> rhs) {
23814    bool overlaps = rhs.n_elems>0 && rhs.elems >= parent.files_elems && rhs.elems < parent.files_elems + parent.files_max;
23815    if (UNLIKELY(overlaps)) {
23816        FatalErrorExit("command.tary_alias  field:command.sandbox.files  comment:'alias error: sub-array is being appended to the whole'");
23817    }
23818    int nnew = rhs.n_elems;
23819    files_Reserve(parent, nnew); // reserve space
23820    int at = parent.files_n;
23821    for (int i = 0; i < nnew; i++) {
23822        new (parent.files_elems + at + i) algo::cstring(rhs[i]);
23823        parent.files_n++;
23824    }
23825    return algo::aryptr<algo::cstring>(parent.files_elems + at, nnew);
23826}
23827
23828// --- command.sandbox.files.Alloc
23829// Reserve space. Insert element at the end
23830// The new element is initialized to a default value
23831algo::cstring& command::files_Alloc(command::sandbox& parent) {
23832    files_Reserve(parent, 1);
23833    int n  = parent.files_n;
23834    int at = n;
23835    algo::cstring *elems = parent.files_elems;
23836    new (elems + at) algo::cstring(); // construct new element, default initializer
23837    parent.files_n = n+1;
23838    return elems[at];
23839}
23840
23841// --- command.sandbox.files.AllocAt
23842// Reserve space for new element, reallocating the array if necessary
23843// Insert new element at specified index. Index must be in range or a fatal error occurs.
23844algo::cstring& command::files_AllocAt(command::sandbox& parent, int at) {
23845    files_Reserve(parent, 1);
23846    int n  = parent.files_n;
23847    if (UNLIKELY(u64(at) >= u64(n+1))) {
23848        FatalErrorExit("command.bad_alloc_at  field:command.sandbox.files  comment:'index out of range'");
23849    }
23850    algo::cstring *elems = parent.files_elems;
23851    memmove(elems + at + 1, elems + at, (n - at) * sizeof(algo::cstring));
23852    new (elems + at) algo::cstring(); // construct element, default initializer
23853    parent.files_n = n+1;
23854    return elems[at];
23855}
23856
23857// --- command.sandbox.files.AllocN
23858// Reserve space. Insert N elements at the end of the array, return pointer to array
23859algo::aryptr<algo::cstring> command::files_AllocN(command::sandbox& parent, int n_elems) {
23860    files_Reserve(parent, n_elems);
23861    int old_n  = parent.files_n;
23862    int new_n = old_n + n_elems;
23863    algo::cstring *elems = parent.files_elems;
23864    for (int i = old_n; i < new_n; i++) {
23865        new (elems + i) algo::cstring(); // construct new element, default initialize
23866    }
23867    parent.files_n = new_n;
23868    return algo::aryptr<algo::cstring>(elems + old_n, n_elems);
23869}
23870
23871// --- command.sandbox.files.Remove
23872// Remove item by index. If index outside of range, do nothing.
23873void command::files_Remove(command::sandbox& parent, u32 i) {
23874    u32 lim = parent.files_n;
23875    algo::cstring *elems = parent.files_elems;
23876    if (i < lim) {
23877        elems[i].~cstring(); // destroy element
23878        memmove(elems + i, elems + (i + 1), sizeof(algo::cstring) * (lim - (i + 1)));
23879        parent.files_n = lim - 1;
23880    }
23881}
23882
23883// --- command.sandbox.files.RemoveAll
23884void command::files_RemoveAll(command::sandbox& parent) {
23885    u32 n = parent.files_n;
23886    while (n > 0) {
23887        n -= 1;
23888        parent.files_elems[n].~cstring();
23889        parent.files_n = n;
23890    }
23891}
23892
23893// --- command.sandbox.files.RemoveLast
23894// Delete last element of array. Do nothing if array is empty.
23895void command::files_RemoveLast(command::sandbox& parent) {
23896    u64 n = parent.files_n;
23897    if (n > 0) {
23898        n -= 1;
23899        files_qFind(parent, u64(n)).~cstring();
23900        parent.files_n = n;
23901    }
23902}
23903
23904// --- command.sandbox.files.AbsReserve
23905// Make sure N elements fit in array. Process dies if out of memory
23906void command::files_AbsReserve(command::sandbox& parent, int n) {
23907    u32 old_max  = parent.files_max;
23908    if (n > i32(old_max)) {
23909        u32 new_max  = i32_Max(i32_Max(old_max * 2, n), 4);
23910        void *new_mem = algo_lib::malloc_ReallocMem(parent.files_elems, old_max * sizeof(algo::cstring), new_max * sizeof(algo::cstring));
23911        if (UNLIKELY(!new_mem)) {
23912            FatalErrorExit("command.tary_nomem  field:command.sandbox.files  comment:'out of memory'");
23913        }
23914        parent.files_elems = (algo::cstring*)new_mem;
23915        parent.files_max = new_max;
23916    }
23917}
23918
23919// --- command.sandbox.files.Setary
23920// Copy contents of RHS to PARENT.
23921void command::files_Setary(command::sandbox& parent, command::sandbox &rhs) {
23922    files_RemoveAll(parent);
23923    int nnew = rhs.files_n;
23924    files_Reserve(parent, nnew); // reserve space
23925    for (int i = 0; i < nnew; i++) { // copy elements over
23926        new (parent.files_elems + i) algo::cstring(files_qFind(rhs, i));
23927        parent.files_n = i + 1;
23928    }
23929}
23930
23931// --- command.sandbox.files.Setary2
23932// Copy specified array into files, discarding previous contents.
23933// If the RHS argument aliases the array (refers to the same memory), throw exception.
23934void command::files_Setary(command::sandbox& parent, const algo::aryptr<algo::cstring> &rhs) {
23935    files_RemoveAll(parent);
23936    files_Addary(parent, rhs);
23937}
23938
23939// --- command.sandbox.files.AllocNVal
23940// Reserve space. Insert N elements at the end of the array, return pointer to array
23941algo::aryptr<algo::cstring> command::files_AllocNVal(command::sandbox& parent, int n_elems, const algo::cstring& val) {
23942    files_Reserve(parent, n_elems);
23943    int old_n  = parent.files_n;
23944    int new_n = old_n + n_elems;
23945    algo::cstring *elems = parent.files_elems;
23946    for (int i = old_n; i < new_n; i++) {
23947        new (elems + i) algo::cstring(val);
23948    }
23949    parent.files_n = new_n;
23950    return algo::aryptr<algo::cstring>(elems + old_n, n_elems);
23951}
23952
23953// --- command.sandbox.files.ReadStrptrMaybe
23954// A single element is read from input string and appended to the array.
23955// If the string contains an error, the array is untouched.
23956// Function returns success value.
23957bool command::files_ReadStrptrMaybe(command::sandbox& parent, algo::strptr in_str) {
23958    bool retval = true;
23959    algo::cstring &elem = files_Alloc(parent);
23960    retval = algo::cstring_ReadStrptrMaybe(elem, in_str);
23961    if (!retval) {
23962        files_RemoveLast(parent);
23963    }
23964    return retval;
23965}
23966
23967// --- command.sandbox..ReadFieldMaybe
23968bool command::sandbox_ReadFieldMaybe(command::sandbox& parent, algo::strptr field, algo::strptr strval) {
23969    bool retval = true;
23970    command::FieldId field_id;
23971    (void)value_SetStrptrMaybe(field_id,algo::Pathcomp(field, ".LL"));
23972    switch(field_id) {
23973        case command_FieldId_in: {
23974            retval = algo::cstring_ReadStrptrMaybe(parent.in, strval);
23975            break;
23976        }
23977        case command_FieldId_name: {
23978            retval = name_ReadStrptrMaybe(parent, strval);
23979            break;
23980        }
23981        case command_FieldId_create: {
23982            retval = bool_ReadStrptrMaybe(parent.create, strval);
23983            break;
23984        }
23985        case command_FieldId_list: {
23986            retval = bool_ReadStrptrMaybe(parent.list, strval);
23987            break;
23988        }
23989        case command_FieldId_reset: {
23990            retval = bool_ReadStrptrMaybe(parent.reset, strval);
23991            break;
23992        }
23993        case command_FieldId_clean: {
23994            retval = bool_ReadStrptrMaybe(parent.clean, strval);
23995            break;
23996        }
23997        case command_FieldId_shell: {
23998            retval = bool_ReadStrptrMaybe(parent.shell, strval);
23999            break;
24000        }
24001        case command_FieldId_del: {
24002            retval = bool_ReadStrptrMaybe(parent.del, strval);
24003            break;
24004        }
24005        case command_FieldId_gc: {
24006            retval = bool_ReadStrptrMaybe(parent.gc, strval);
24007            break;
24008        }
24009        case command_FieldId_cmd: {
24010            retval = cmd_ReadStrptrMaybe(parent, strval);
24011            break;
24012        }
24013        case command_FieldId_diff: {
24014            retval = bool_ReadStrptrMaybe(parent.diff, strval);
24015            break;
24016        }
24017        case command_FieldId_files: {
24018            retval = files_ReadStrptrMaybe(parent, strval);
24019            break;
24020        }
24021        case command_FieldId_refs: {
24022            retval = algo::cstring_ReadStrptrMaybe(parent.refs, strval);
24023            break;
24024        }
24025        case command_FieldId_q: {
24026            retval = bool_ReadStrptrMaybe(parent.q, strval);
24027            break;
24028        }
24029        default: break;
24030    }
24031    if (!retval) {
24032        algo_lib::AppendErrtext("attr",field);
24033    }
24034    return retval;
24035}
24036
24037// --- command.sandbox..ReadTupleMaybe
24038// Read fields of command::sandbox from attributes of ascii tuple TUPLE
24039bool command::sandbox_ReadTupleMaybe(command::sandbox &parent, algo::Tuple &tuple) {
24040    bool retval = true;
24041    int anon_idx = 0;
24042    ind_beg(algo::Tuple_attrs_curs,attr,tuple) {
24043        if (ch_N(attr.name) == 0) {
24044            attr.name = sandbox_GetAnon(parent, anon_idx++);
24045        }
24046        retval = sandbox_ReadFieldMaybe(parent, attr.name, attr.value);
24047        if (!retval) {
24048            break;
24049        }
24050    }ind_end;
24051    return retval;
24052}
24053
24054// --- command.sandbox..Init
24055// Set all fields to initial values.
24056void command::sandbox_Init(command::sandbox& parent) {
24057    parent.in = algo::strptr("data");
24058    parent.create = bool(false);
24059    parent.list = bool(false);
24060    parent.reset = bool(false);
24061    parent.clean = bool(false);
24062    parent.shell = bool(false);
24063    parent.del = bool(false);
24064    parent.gc = bool(false);
24065    parent.cmd_elems 	= 0; // (command.sandbox.cmd)
24066    parent.cmd_n     	= 0; // (command.sandbox.cmd)
24067    parent.cmd_max   	= 0; // (command.sandbox.cmd)
24068    parent.diff = bool(false);
24069    parent.files_elems 	= 0; // (command.sandbox.files)
24070    parent.files_n     	= 0; // (command.sandbox.files)
24071    parent.files_max   	= 0; // (command.sandbox.files)
24072    parent.refs = algo::strptr("HEAD");
24073    parent.q = bool(false);
24074}
24075
24076// --- command.sandbox..Uninit
24077void command::sandbox_Uninit(command::sandbox& parent) {
24078    command::sandbox &row = parent; (void)row;
24079
24080    // command.sandbox.files.Uninit (Tary)  //Shell regx to diff
24081    // remove all elements from command.sandbox.files
24082    files_RemoveAll(parent);
24083    // free memory for Tary command.sandbox.files
24084    algo_lib::malloc_FreeMem(parent.files_elems, sizeof(algo::cstring)*parent.files_max); // (command.sandbox.files)
24085
24086    // command.sandbox.cmd.Uninit (Tary)  //Command to execute in sandbox
24087    // remove all elements from command.sandbox.cmd
24088    cmd_RemoveAll(parent);
24089    // free memory for Tary command.sandbox.cmd
24090    algo_lib::malloc_FreeMem(parent.cmd_elems, sizeof(algo::cstring)*parent.cmd_max); // (command.sandbox.cmd)
24091}
24092
24093// --- command.sandbox..ToCmdline
24094// Convenience function that returns a full command line
24095// Assume command is in a directory called bin
24096tempstr command::sandbox_ToCmdline(command::sandbox& row) {
24097    tempstr ret;
24098    ret << "bin/sandbox ";
24099    sandbox_PrintArgv(row, ret);
24100    // inherit less intense verbose, debug options
24101    for (int i = 1; i < algo_lib::_db.cmdline.verbose; i++) {
24102        ret << " -verbose";
24103    }
24104    for (int i = 1; i < algo_lib::_db.cmdline.debug; i++) {
24105        ret << " -debug";
24106    }
24107    return ret;
24108}
24109
24110// --- command.sandbox..PrintArgv
24111// print string representation of ROW to string STR
24112// cfmt:command.sandbox.Argv  printfmt:Tuple
24113void command::sandbox_PrintArgv(command::sandbox& row, algo::cstring& str) {
24114    algo::tempstr temp;
24115    (void)temp;
24116    (void)str;
24117    if (!(row.in == "data")) {
24118        ch_RemoveAll(temp);
24119        cstring_Print(row.in, temp);
24120        str << " -in:";
24121        strptr_PrintBash(temp,str);
24122    }
24123    ch_RemoveAll(temp);
24124    command::name_Print(const_cast<command::sandbox&>(row), temp);
24125    str << " -name:";
24126    strptr_PrintBash(temp,str);
24127    if (!(row.create == false)) {
24128        ch_RemoveAll(temp);
24129        bool_Print(row.create, temp);
24130        str << " -create:";
24131        strptr_PrintBash(temp,str);
24132    }
24133    if (!(row.list == false)) {
24134        ch_RemoveAll(temp);
24135        bool_Print(row.list, temp);
24136        str << " -list:";
24137        strptr_PrintBash(temp,str);
24138    }
24139    if (!(row.reset == false)) {
24140        ch_RemoveAll(temp);
24141        bool_Print(row.reset, temp);
24142        str << " -reset:";
24143        strptr_PrintBash(temp,str);
24144    }
24145    if (!(row.clean == false)) {
24146        ch_RemoveAll(temp);
24147        bool_Print(row.clean, temp);
24148        str << " -clean:";
24149        strptr_PrintBash(temp,str);
24150    }
24151    if (!(row.shell == false)) {
24152        ch_RemoveAll(temp);
24153        bool_Print(row.shell, temp);
24154        str << " -shell:";
24155        strptr_PrintBash(temp,str);
24156    }
24157    if (!(row.del == false)) {
24158        ch_RemoveAll(temp);
24159        bool_Print(row.del, temp);
24160        str << " -del:";
24161        strptr_PrintBash(temp,str);
24162    }
24163    if (!(row.gc == false)) {
24164        ch_RemoveAll(temp);
24165        bool_Print(row.gc, temp);
24166        str << " -gc:";
24167        strptr_PrintBash(temp,str);
24168    }
24169    ind_beg(sandbox_cmd_curs,value,row) {
24170        ch_RemoveAll(temp);
24171        cstring_Print(value, temp);
24172        str << " -cmd:";
24173        strptr_PrintBash(temp,str);
24174    }ind_end;
24175    if (!(row.diff == false)) {
24176        ch_RemoveAll(temp);
24177        bool_Print(row.diff, temp);
24178        str << " -diff:";
24179        strptr_PrintBash(temp,str);
24180    }
24181    ind_beg(sandbox_files_curs,value,row) {
24182        ch_RemoveAll(temp);
24183        cstring_Print(value, temp);
24184        str << " -files:";
24185        strptr_PrintBash(temp,str);
24186    }ind_end;
24187    if (!(row.refs == "HEAD")) {
24188        ch_RemoveAll(temp);
24189        cstring_Print(row.refs, temp);
24190        str << " -refs:";
24191        strptr_PrintBash(temp,str);
24192    }
24193    if (!(row.q == false)) {
24194        ch_RemoveAll(temp);
24195        bool_Print(row.q, temp);
24196        str << " -q:";
24197        strptr_PrintBash(temp,str);
24198    }
24199}
24200
24201// --- command.sandbox..GetAnon
24202algo::strptr command::sandbox_GetAnon(command::sandbox &parent, i32 idx) {
24203    (void)parent;//only to avoid -Wunused-parameter
24204    switch(idx) {
24205        case(0): return strptr("name", 4);
24206        default: return strptr("cmd", 3);
24207    }
24208}
24209
24210// --- command.sandbox..NArgs
24211// Used with command lines
24212// Return # of command-line arguments that must follow this argument
24213// If FIELD is invalid, return -1
24214i32 command::sandbox_NArgs(command::FieldId field, algo::strptr& out_dflt, bool* out_anon) {
24215    i32 retval = 1;
24216    switch (field) {
24217        case command_FieldId_in: { // $comment
24218            *out_anon = false;
24219        } break;
24220        case command_FieldId_name: { // $comment
24221            *out_anon = true;
24222        } break;
24223        case command_FieldId_create: { // $comment
24224            *out_anon = false;
24225            retval=0;
24226            out_dflt="Y";
24227        } break;
24228        case command_FieldId_list: { // bool: no argument required but value may be specified as create:Y
24229            *out_anon = false;
24230            retval=0;
24231            out_dflt="Y";
24232        } break;
24233        case command_FieldId_reset: { // bool: no argument required but value may be specified as list:Y
24234            *out_anon = false;
24235            retval=0;
24236            out_dflt="Y";
24237        } break;
24238        case command_FieldId_clean: { // bool: no argument required but value may be specified as reset:Y
24239            *out_anon = false;
24240            retval=0;
24241            out_dflt="Y";
24242        } break;
24243        case command_FieldId_shell: { // bool: no argument required but value may be specified as clean:Y
24244            *out_anon = false;
24245            retval=0;
24246            out_dflt="Y";
24247        } break;
24248        case command_FieldId_del: { // bool: no argument required but value may be specified as shell:Y
24249            *out_anon = false;
24250            retval=0;
24251            out_dflt="Y";
24252        } break;
24253        case command_FieldId_gc: { // bool: no argument required but value may be specified as del:Y
24254            *out_anon = false;
24255            retval=0;
24256            out_dflt="Y";
24257        } break;
24258        case command_FieldId_cmd: { // bool: no argument required but value may be specified as gc:Y
24259            *out_anon = true;
24260        } break;
24261        case command_FieldId_diff: { // bool: no argument required but value may be specified as gc:Y
24262            *out_anon = false;
24263            retval=0;
24264            out_dflt="Y";
24265        } break;
24266        case command_FieldId_files: { // bool: no argument required but value may be specified as diff:Y
24267            *out_anon = false;
24268        } break;
24269        case command_FieldId_refs: { // bool: no argument required but value may be specified as diff:Y
24270            *out_anon = false;
24271        } break;
24272        case command_FieldId_q: { // bool: no argument required but value may be specified as diff:Y
24273            *out_anon = false;
24274            retval=0;
24275            out_dflt="Y";
24276        } break;
24277        default:
24278        retval=-1; // unrecognized
24279    }
24280    return retval;
24281}
24282
24283// --- command.sandbox_proc.sandbox.Start
24284// Start subprocess
24285// If subprocess already running, do nothing. Otherwise, start it
24286int command::sandbox_Start(command::sandbox_proc& parent) {
24287    int retval = 0;
24288    if (parent.pid == 0) {
24289        verblog(sandbox_ToCmdline(parent)); // maybe print command
24290#ifdef WIN32
24291        algo_lib::ResolveExecFname(parent.path);
24292        tempstr cmdline(sandbox_ToCmdline(parent));
24293        parent.pid = dospawn(Zeroterm(parent.path),Zeroterm(cmdline),parent.timeout,parent.fstdin,parent.fstdout,parent.fstderr);
24294#else
24295        parent.pid = fork();
24296        if (parent.pid == 0) { // child
24297            algo_lib::DieWithParent();
24298            if (parent.timeout > 0) {
24299                alarm(parent.timeout);
24300            }
24301            if (retval==0) retval=algo_lib::ApplyRedirect(parent.fstdin , 0);
24302            if (retval==0) retval=algo_lib::ApplyRedirect(parent.fstdout, 1);
24303            if (retval==0) retval=algo_lib::ApplyRedirect(parent.fstderr, 2);
24304            if (retval==0) retval= sandbox_Execv(parent);
24305            if (retval != 0) { // if start fails, print error
24306                int err=errno;
24307                prerr("command.sandbox_execv"
24308                <<Keyval("errno",err)
24309                <<Keyval("errstr",strerror(err))
24310                <<Keyval("comment","Execv failed"));
24311            }
24312            _exit(127); // if failed to start, exit anyway
24313        } else if (parent.pid == -1) {
24314            retval = errno; // failed to fork
24315        }
24316#endif
24317    }
24318    parent.status = parent.pid > 0 ? 0 : -1; // if didn't start, set error status
24319    return retval;
24320}
24321
24322// --- command.sandbox_proc.sandbox.StartRead
24323// Start subprocess & Read output
24324algo::Fildes command::sandbox_StartRead(command::sandbox_proc& parent, algo_lib::FFildes &read) {
24325    int pipefd[2];
24326    int rc=pipe(pipefd);
24327    (void)rc;
24328    read.fd.value = pipefd[0];
24329    parent.fstdout  << ">&" << pipefd[1];
24330    sandbox_Start(parent);
24331    (void)close(pipefd[1]);
24332    return read.fd;
24333}
24334
24335// --- command.sandbox_proc.sandbox.Kill
24336// Kill subprocess and wait
24337void command::sandbox_Kill(command::sandbox_proc& parent) {
24338    if (parent.pid != 0) {
24339        kill(parent.pid,9);
24340        sandbox_Wait(parent);
24341    }
24342}
24343
24344// --- command.sandbox_proc.sandbox.Wait
24345// Wait for subprocess to return
24346void command::sandbox_Wait(command::sandbox_proc& parent) {
24347    if (parent.pid > 0) {
24348        int wait_flags = 0;
24349        int wait_status = 0;
24350        int rc = -1;
24351        do {
24352            // really wait for subprocess to exit
24353            rc = waitpid(parent.pid,&wait_status,wait_flags);
24354        } while (rc==-1 && errno==EINTR);
24355        if (rc == parent.pid) {
24356            parent.status = wait_status;
24357            parent.pid = 0;
24358        }
24359    }
24360}
24361
24362// --- command.sandbox_proc.sandbox.Exec
24363// Start + Wait
24364// Execute subprocess and return exit code
24365int command::sandbox_Exec(command::sandbox_proc& parent) {
24366    sandbox_Start(parent);
24367    sandbox_Wait(parent);
24368    return parent.status;
24369}
24370
24371// --- command.sandbox_proc.sandbox.ExecX
24372// Start + Wait, throw exception on error
24373// Execute subprocess; throw human-readable exception on error
24374void command::sandbox_ExecX(command::sandbox_proc& parent) {
24375    int rc = sandbox_Exec(parent);
24376    vrfy(rc==0, tempstr() << "algo_lib.exec" << Keyval("cmd",sandbox_ToCmdline(parent))
24377    << Keyval("comment",algo::DescribeWaitStatus(parent.status)));
24378}
24379
24380// --- command.sandbox_proc.sandbox.Execv
24381// Call execv()
24382// Call execv with specified parameters
24383int command::sandbox_Execv(command::sandbox_proc& parent) {
24384    int ret = 0;
24385    algo::StringAry args;
24386    sandbox_ToArgv(parent, args);
24387    char **argv = (char**)alloca((ary_N(args)+1)*sizeof(*argv));
24388    ind_beg(algo::StringAry_ary_curs,arg,args) {
24389        argv[ind_curs(arg).index] = Zeroterm(arg);
24390    }ind_end;
24391    argv[ary_N(args)] = NULL;
24392    // if parent.path is relative, search for it in PATH
24393    algo_lib::ResolveExecFname(parent.path);
24394    ret = execv(Zeroterm(parent.path),argv);
24395    return ret;
24396}
24397
24398// --- command.sandbox_proc.sandbox.ToCmdline
24399algo::tempstr command::sandbox_ToCmdline(command::sandbox_proc& parent) {
24400    algo::tempstr retval;
24401    retval << parent.path << " ";
24402    command::sandbox_PrintArgv(parent.cmd,retval);
24403    if (ch_N(parent.fstdin)) {
24404        retval << " " << parent.fstdin;
24405    }
24406    if (ch_N(parent.fstdout)) {
24407        retval << " " << parent.fstdout;
24408    }
24409    if (ch_N(parent.fstderr)) {
24410        retval << " 2" << parent.fstderr;
24411    }
24412    return retval;
24413}
24414
24415// --- command.sandbox_proc.sandbox.ToArgv
24416// Form array from the command line
24417void command::sandbox_ToArgv(command::sandbox_proc& parent, algo::StringAry& args) {
24418    ary_RemoveAll(args);
24419    ary_Alloc(args) << parent.path;
24420
24421    if (parent.cmd.in != "data") {
24422        cstring *arg = &ary_Alloc(args);
24423        *arg << "-in:";
24424        cstring_Print(parent.cmd.in, *arg);
24425    }
24426
24427    if (true) {
24428        cstring *arg = &ary_Alloc(args);
24429        *arg << "-name:";
24430        command::name_Print(parent.cmd, *arg);
24431    }
24432
24433    if (parent.cmd.create != false) {
24434        cstring *arg = &ary_Alloc(args);
24435        *arg << "-create:";
24436        bool_Print(parent.cmd.create, *arg);
24437    }
24438
24439    if (parent.cmd.list != false) {
24440        cstring *arg = &ary_Alloc(args);
24441        *arg << "-list:";
24442        bool_Print(parent.cmd.list, *arg);
24443    }
24444
24445    if (parent.cmd.reset != false) {
24446        cstring *arg = &ary_Alloc(args);
24447        *arg << "-reset:";
24448        bool_Print(parent.cmd.reset, *arg);
24449    }
24450
24451    if (parent.cmd.clean != false) {
24452        cstring *arg = &ary_Alloc(args);
24453        *arg << "-clean:";
24454        bool_Print(parent.cmd.clean, *arg);
24455    }
24456
24457    if (parent.cmd.shell != false) {
24458        cstring *arg = &ary_Alloc(args);
24459        *arg << "-shell:";
24460        bool_Print(parent.cmd.shell, *arg);
24461    }
24462
24463    if (parent.cmd.del != false) {
24464        cstring *arg = &ary_Alloc(args);
24465        *arg << "-del:";
24466        bool_Print(parent.cmd.del, *arg);
24467    }
24468
24469    if (parent.cmd.gc != false) {
24470        cstring *arg = &ary_Alloc(args);
24471        *arg << "-gc:";
24472        bool_Print(parent.cmd.gc, *arg);
24473    }
24474    ind_beg(command::sandbox_cmd_curs,value,parent.cmd) {
24475        cstring *arg = &ary_Alloc(args);
24476        *arg << "-cmd:";
24477        cstring_Print(value, *arg);
24478    }ind_end;
24479
24480    if (parent.cmd.diff != false) {
24481        cstring *arg = &ary_Alloc(args);
24482        *arg << "-diff:";
24483        bool_Print(parent.cmd.diff, *arg);
24484    }
24485    ind_beg(command::sandbox_files_curs,value,parent.cmd) {
24486        cstring *arg = &ary_Alloc(args);
24487        *arg << "-files:";
24488        cstring_Print(value, *arg);
24489    }ind_end;
24490
24491    if (parent.cmd.refs != "HEAD") {
24492        cstring *arg = &ary_Alloc(args);
24493        *arg << "-refs:";
24494        cstring_Print(parent.cmd.refs, *arg);
24495    }
24496
24497    if (parent.cmd.q != false) {
24498        cstring *arg = &ary_Alloc(args);
24499        *arg << "-q:";
24500        bool_Print(parent.cmd.q, *arg);
24501    }
24502    for (int i=1; i < algo_lib::_db.cmdline.verbose; ++i) {
24503        ary_Alloc(args) << "-verbose";
24504    }
24505}
24506
24507// --- command.sandbox_proc..Uninit
24508void command::sandbox_proc_Uninit(command::sandbox_proc& parent) {
24509    command::sandbox_proc &row = parent; (void)row;
24510
24511    // command.sandbox_proc.sandbox.Uninit (Exec)  //
24512    sandbox_Kill(parent); // kill child, ensure forward progress
24513}
24514
24515// --- command.spnx..ReadFieldMaybe
24516bool command::spnx_ReadFieldMaybe(command::spnx& parent, algo::strptr field, algo::strptr strval) {
24517    bool retval = true;
24518    command::FieldId field_id;
24519    (void)value_SetStrptrMaybe(field_id,field);
24520    switch(field_id) {
24521        case command_FieldId_in: {
24522            retval = algo::cstring_ReadStrptrMaybe(parent.in, strval);
24523            break;
24524        }
24525        case command_FieldId_site: {
24526            retval = algo::Smallstr50_ReadStrptrMaybe(parent.site, strval);
24527            break;
24528        }
24529        case command_FieldId_build: {
24530            retval = bool_ReadStrptrMaybe(parent.build, strval);
24531            break;
24532        }
24533        case command_FieldId_builder: {
24534            retval = algo::Smallstr50_ReadStrptrMaybe(parent.builder, strval);
24535            break;
24536        }
24537        case command_FieldId_build_fail_on_warning: {
24538            retval = bool_ReadStrptrMaybe(parent.build_fail_on_warning, strval);
24539            break;
24540        }
24541        case command_FieldId_build_quiet: {
24542            retval = bool_ReadStrptrMaybe(parent.build_quiet, strval);
24543            break;
24544        }
24545        case command_FieldId_jobs: {
24546            retval = u32_ReadStrptrMaybe(parent.jobs, strval);
24547            break;
24548        }
24549        case command_FieldId_preserve_footer: {
24550            retval = bool_ReadStrptrMaybe(parent.preserve_footer, strval);
24551            break;
24552        }
24553        case command_FieldId_clean: {
24554            retval = bool_ReadStrptrMaybe(parent.clean, strval);
24555            break;
24556        }
24557        case command_FieldId_serve: {
24558            retval = bool_ReadStrptrMaybe(parent.serve, strval);
24559            break;
24560        }
24561        case command_FieldId_serve_dctrhost: {
24562            retval = algo::Smallstr50_ReadStrptrMaybe(parent.serve_dctrhost, strval);
24563            break;
24564        }
24565        case command_FieldId_serve_pages: {
24566            retval = bool_ReadStrptrMaybe(parent.serve_pages, strval);
24567            break;
24568        }
24569        case command_FieldId_serve_verify: {
24570            retval = bool_ReadStrptrMaybe(parent.serve_verify, strval);
24571            break;
24572        }
24573        case command_FieldId_workdir: {
24574            retval = algo::cstring_ReadStrptrMaybe(parent.workdir, strval);
24575            break;
24576        }
24577        case command_FieldId_dry_run: {
24578            retval = bool_ReadStrptrMaybe(parent.dry_run, strval);
24579            break;
24580        }
24581        case command_FieldId_opts_numbered: {
24582            retval = bool_ReadStrptrMaybe(parent.opts_numbered, strval);
24583            break;
24584        }
24585        default: break;
24586    }
24587    if (!retval) {
24588        algo_lib::AppendErrtext("attr",field);
24589    }
24590    return retval;
24591}
24592
24593// --- command.spnx..ReadTupleMaybe
24594// Read fields of command::spnx from attributes of ascii tuple TUPLE
24595bool command::spnx_ReadTupleMaybe(command::spnx &parent, algo::Tuple &tuple) {
24596    bool retval = true;
24597    ind_beg(algo::Tuple_attrs_curs,attr,tuple) {
24598        retval = spnx_ReadFieldMaybe(parent, attr.name, attr.value);
24599        if (!retval) {
24600            break;
24601        }
24602    }ind_end;
24603    return retval;
24604}
24605
24606// --- command.spnx..Init
24607// Set all fields to initial values.
24608void command::spnx_Init(command::spnx& parent) {
24609    parent.in = algo::strptr("data");
24610    parent.site = algo::strptr("OpenacrDoc");
24611    parent.build = bool(false);
24612    parent.builder = algo::strptr("html");
24613    parent.build_fail_on_warning = bool(false);
24614    parent.build_quiet = bool(false);
24615    parent.jobs = u32(0);
24616    parent.preserve_footer = bool(false);
24617    parent.clean = bool(false);
24618    parent.serve = bool(false);
24619    parent.serve_dctrhost = algo::strptr("");
24620    parent.serve_pages = bool(false);
24621    parent.serve_verify = bool(false);
24622    parent.workdir = algo::strptr("temp/");
24623    parent.dry_run = bool(false);
24624    parent.opts_numbered = bool(false);
24625}
24626
24627// --- command.spnx..ToCmdline
24628// Convenience function that returns a full command line
24629// Assume command is in a directory called bin
24630tempstr command::spnx_ToCmdline(command::spnx& row) {
24631    tempstr ret;
24632    ret << "bin/spnx ";
24633    spnx_PrintArgv(row, ret);
24634    // inherit less intense verbose, debug options
24635    for (int i = 1; i < algo_lib::_db.cmdline.verbose; i++) {
24636        ret << " -verbose";
24637    }
24638    for (int i = 1; i < algo_lib::_db.cmdline.debug; i++) {
24639        ret << " -debug";
24640    }
24641    return ret;
24642}
24643
24644// --- command.spnx..PrintArgv
24645// print string representation of ROW to string STR
24646// cfmt:command.spnx.Argv  printfmt:Tuple
24647void command::spnx_PrintArgv(command::spnx& row, algo::cstring& str) {
24648    algo::tempstr temp;
24649    (void)temp;
24650    (void)str;
24651    if (!(row.in == "data")) {
24652        ch_RemoveAll(temp);
24653        cstring_Print(row.in, temp);
24654        str << " -in:";
24655        strptr_PrintBash(temp,str);
24656    }
24657    if (!(row.site == "OpenacrDoc")) {
24658        ch_RemoveAll(temp);
24659        Smallstr50_Print(row.site, temp);
24660        str << " -site:";
24661        strptr_PrintBash(temp,str);
24662    }
24663    if (!(row.build == false)) {
24664        ch_RemoveAll(temp);
24665        bool_Print(row.build, temp);
24666        str << " -build:";
24667        strptr_PrintBash(temp,str);
24668    }
24669    if (!(row.builder == "html")) {
24670        ch_RemoveAll(temp);
24671        Smallstr50_Print(row.builder, temp);
24672        str << " -builder:";
24673        strptr_PrintBash(temp,str);
24674    }
24675    if (!(row.build_fail_on_warning == false)) {
24676        ch_RemoveAll(temp);
24677        bool_Print(row.build_fail_on_warning, temp);
24678        str << " -build_fail_on_warning:";
24679        strptr_PrintBash(temp,str);
24680    }
24681    if (!(row.build_quiet == false)) {
24682        ch_RemoveAll(temp);
24683        bool_Print(row.build_quiet, temp);
24684        str << " -build_quiet:";
24685        strptr_PrintBash(temp,str);
24686    }
24687    if (!(row.jobs == 0)) {
24688        ch_RemoveAll(temp);
24689        u32_Print(row.jobs, temp);
24690        str << " -jobs:";
24691        strptr_PrintBash(temp,str);
24692    }
24693    if (!(row.preserve_footer == false)) {
24694        ch_RemoveAll(temp);
24695        bool_Print(row.preserve_footer, temp);
24696        str << " -preserve_footer:";
24697        strptr_PrintBash(temp,str);
24698    }
24699    if (!(row.clean == false)) {
24700        ch_RemoveAll(temp);
24701        bool_Print(row.clean, temp);
24702        str << " -clean:";
24703        strptr_PrintBash(temp,str);
24704    }
24705    if (!(row.serve == false)) {
24706        ch_RemoveAll(temp);
24707        bool_Print(row.serve, temp);
24708        str << " -serve:";
24709        strptr_PrintBash(temp,str);
24710    }
24711    if (!(row.serve_dctrhost == "")) {
24712        ch_RemoveAll(temp);
24713        Smallstr50_Print(row.serve_dctrhost, temp);
24714        str << " -serve_dctrhost:";
24715        strptr_PrintBash(temp,str);
24716    }
24717    if (!(row.serve_pages == false)) {
24718        ch_RemoveAll(temp);
24719        bool_Print(row.serve_pages, temp);
24720        str << " -serve_pages:";
24721        strptr_PrintBash(temp,str);
24722    }
24723    if (!(row.serve_verify == false)) {
24724        ch_RemoveAll(temp);
24725        bool_Print(row.serve_verify, temp);
24726        str << " -serve_verify:";
24727        strptr_PrintBash(temp,str);
24728    }
24729    if (!(row.workdir == "temp/")) {
24730        ch_RemoveAll(temp);
24731        cstring_Print(row.workdir, temp);
24732        str << " -workdir:";
24733        strptr_PrintBash(temp,str);
24734    }
24735    if (!(row.dry_run == false)) {
24736        ch_RemoveAll(temp);
24737        bool_Print(row.dry_run, temp);
24738        str << " -dry_run:";
24739        strptr_PrintBash(temp,str);
24740    }
24741    if (!(row.opts_numbered == false)) {
24742        ch_RemoveAll(temp);
24743        bool_Print(row.opts_numbered, temp);
24744        str << " -opts_numbered:";
24745        strptr_PrintBash(temp,str);
24746    }
24747}
24748
24749// --- command.spnx..NArgs
24750// Used with command lines
24751// Return # of command-line arguments that must follow this argument
24752// If FIELD is invalid, return -1
24753i32 command::spnx_NArgs(command::FieldId field, algo::strptr& out_dflt, bool* out_anon) {
24754    i32 retval = 1;
24755    switch (field) {
24756        case command_FieldId_in: { // $comment
24757            *out_anon = false;
24758        } break;
24759        case command_FieldId_site: { // $comment
24760            *out_anon = false;
24761        } break;
24762        case command_FieldId_build: { // $comment
24763            *out_anon = false;
24764            retval=0;
24765            out_dflt="Y";
24766        } break;
24767        case command_FieldId_builder: { // bool: no argument required but value may be specified as build:Y
24768            *out_anon = false;
24769        } break;
24770        case command_FieldId_build_fail_on_warning: { // bool: no argument required but value may be specified as build:Y
24771            *out_anon = false;
24772            retval=0;
24773            out_dflt="Y";
24774        } break;
24775        case command_FieldId_build_quiet: { // bool: no argument required but value may be specified as build_fail_on_warning:Y
24776            *out_anon = false;
24777            retval=0;
24778            out_dflt="Y";
24779        } break;
24780        case command_FieldId_jobs: { // bool: no argument required but value may be specified as build_quiet:Y
24781            *out_anon = false;
24782        } break;
24783        case command_FieldId_preserve_footer: { // bool: no argument required but value may be specified as build_quiet:Y
24784            *out_anon = false;
24785            retval=0;
24786            out_dflt="Y";
24787        } break;
24788        case command_FieldId_clean: { // bool: no argument required but value may be specified as preserve_footer:Y
24789            *out_anon = false;
24790            retval=0;
24791            out_dflt="Y";
24792        } break;
24793        case command_FieldId_serve: { // bool: no argument required but value may be specified as clean:Y
24794            *out_anon = false;
24795            retval=0;
24796            out_dflt="Y";
24797        } break;
24798        case command_FieldId_serve_dctrhost: { // bool: no argument required but value may be specified as serve:Y
24799            *out_anon = false;
24800        } break;
24801        case command_FieldId_serve_pages: { // bool: no argument required but value may be specified as serve:Y
24802            *out_anon = false;
24803            retval=0;
24804            out_dflt="Y";
24805        } break;
24806        case command_FieldId_serve_verify: { // bool: no argument required but value may be specified as serve_pages:Y
24807            *out_anon = false;
24808            retval=0;
24809            out_dflt="Y";
24810        } break;
24811        case command_FieldId_workdir: { // bool: no argument required but value may be specified as serve_verify:Y
24812            *out_anon = false;
24813        } break;
24814        case command_FieldId_dry_run: { // bool: no argument required but value may be specified as serve_verify:Y
24815            *out_anon = false;
24816            retval=0;
24817            out_dflt="Y";
24818        } break;
24819        case command_FieldId_opts_numbered: { // bool: no argument required but value may be specified as dry_run:Y
24820            *out_anon = false;
24821            retval=0;
24822            out_dflt="Y";
24823        } break;
24824        default:
24825        retval=-1; // unrecognized
24826    }
24827    return retval;
24828}
24829
24830// --- command.spnx_proc.spnx.Start
24831// Start subprocess
24832// If subprocess already running, do nothing. Otherwise, start it
24833int command::spnx_Start(command::spnx_proc& parent) {
24834    int retval = 0;
24835    if (parent.pid == 0) {
24836        verblog(spnx_ToCmdline(parent)); // maybe print command
24837#ifdef WIN32
24838        algo_lib::ResolveExecFname(parent.path);
24839        tempstr cmdline(spnx_ToCmdline(parent));
24840        parent.pid = dospawn(Zeroterm(parent.path),Zeroterm(cmdline),parent.timeout,parent.fstdin,parent.fstdout,parent.fstderr);
24841#else
24842        parent.pid = fork();
24843        if (parent.pid == 0) { // child
24844            algo_lib::DieWithParent();
24845            if (parent.timeout > 0) {
24846                alarm(parent.timeout);
24847            }
24848            if (retval==0) retval=algo_lib::ApplyRedirect(parent.fstdin , 0);
24849            if (retval==0) retval=algo_lib::ApplyRedirect(parent.fstdout, 1);
24850            if (retval==0) retval=algo_lib::ApplyRedirect(parent.fstderr, 2);
24851            if (retval==0) retval= spnx_Execv(parent);
24852            if (retval != 0) { // if start fails, print error
24853                int err=errno;
24854                prerr("command.spnx_execv"
24855                <<Keyval("errno",err)
24856                <<Keyval("errstr",strerror(err))
24857                <<Keyval("comment","Execv failed"));
24858            }
24859            _exit(127); // if failed to start, exit anyway
24860        } else if (parent.pid == -1) {
24861            retval = errno; // failed to fork
24862        }
24863#endif
24864    }
24865    parent.status = parent.pid > 0 ? 0 : -1; // if didn't start, set error status
24866    return retval;
24867}
24868
24869// --- command.spnx_proc.spnx.StartRead
24870// Start subprocess & Read output
24871algo::Fildes command::spnx_StartRead(command::spnx_proc& parent, algo_lib::FFildes &read) {
24872    int pipefd[2];
24873    int rc=pipe(pipefd);
24874    (void)rc;
24875    read.fd.value = pipefd[0];
24876    parent.fstdout  << ">&" << pipefd[1];
24877    spnx_Start(parent);
24878    (void)close(pipefd[1]);
24879    return read.fd;
24880}
24881
24882// --- command.spnx_proc.spnx.Kill
24883// Kill subprocess and wait
24884void command::spnx_Kill(command::spnx_proc& parent) {
24885    if (parent.pid != 0) {
24886        kill(parent.pid,9);
24887        spnx_Wait(parent);
24888    }
24889}
24890
24891// --- command.spnx_proc.spnx.Wait
24892// Wait for subprocess to return
24893void command::spnx_Wait(command::spnx_proc& parent) {
24894    if (parent.pid > 0) {
24895        int wait_flags = 0;
24896        int wait_status = 0;
24897        int rc = -1;
24898        do {
24899            // really wait for subprocess to exit
24900            rc = waitpid(parent.pid,&wait_status,wait_flags);
24901        } while (rc==-1 && errno==EINTR);
24902        if (rc == parent.pid) {
24903            parent.status = wait_status;
24904            parent.pid = 0;
24905        }
24906    }
24907}
24908
24909// --- command.spnx_proc.spnx.Exec
24910// Start + Wait
24911// Execute subprocess and return exit code
24912int command::spnx_Exec(command::spnx_proc& parent) {
24913    spnx_Start(parent);
24914    spnx_Wait(parent);
24915    return parent.status;
24916}
24917
24918// --- command.spnx_proc.spnx.ExecX
24919// Start + Wait, throw exception on error
24920// Execute subprocess; throw human-readable exception on error
24921void command::spnx_ExecX(command::spnx_proc& parent) {
24922    int rc = spnx_Exec(parent);
24923    vrfy(rc==0, tempstr() << "algo_lib.exec" << Keyval("cmd",spnx_ToCmdline(parent))
24924    << Keyval("comment",algo::DescribeWaitStatus(parent.status)));
24925}
24926
24927// --- command.spnx_proc.spnx.Execv
24928// Call execv()
24929// Call execv with specified parameters
24930int command::spnx_Execv(command::spnx_proc& parent) {
24931    int ret = 0;
24932    algo::StringAry args;
24933    spnx_ToArgv(parent, args);
24934    char **argv = (char**)alloca((ary_N(args)+1)*sizeof(*argv));
24935    ind_beg(algo::StringAry_ary_curs,arg,args) {
24936        argv[ind_curs(arg).index] = Zeroterm(arg);
24937    }ind_end;
24938    argv[ary_N(args)] = NULL;
24939    // if parent.path is relative, search for it in PATH
24940    algo_lib::ResolveExecFname(parent.path);
24941    ret = execv(Zeroterm(parent.path),argv);
24942    return ret;
24943}
24944
24945// --- command.spnx_proc.spnx.ToCmdline
24946algo::tempstr command::spnx_ToCmdline(command::spnx_proc& parent) {
24947    algo::tempstr retval;
24948    retval << parent.path << " ";
24949    command::spnx_PrintArgv(parent.cmd,retval);
24950    if (ch_N(parent.fstdin)) {
24951        retval << " " << parent.fstdin;
24952    }
24953    if (ch_N(parent.fstdout)) {
24954        retval << " " << parent.fstdout;
24955    }
24956    if (ch_N(parent.fstderr)) {
24957        retval << " 2" << parent.fstderr;
24958    }
24959    return retval;
24960}
24961
24962// --- command.spnx_proc.spnx.ToArgv
24963// Form array from the command line
24964void command::spnx_ToArgv(command::spnx_proc& parent, algo::StringAry& args) {
24965    ary_RemoveAll(args);
24966    ary_Alloc(args) << parent.path;
24967
24968    if (parent.cmd.in != "data") {
24969        cstring *arg = &ary_Alloc(args);
24970        *arg << "-in:";
24971        cstring_Print(parent.cmd.in, *arg);
24972    }
24973
24974    if (parent.cmd.site != "OpenacrDoc") {
24975        cstring *arg = &ary_Alloc(args);
24976        *arg << "-site:";
24977        Smallstr50_Print(parent.cmd.site, *arg);
24978    }
24979
24980    if (parent.cmd.build != false) {
24981        cstring *arg = &ary_Alloc(args);
24982        *arg << "-build:";
24983        bool_Print(parent.cmd.build, *arg);
24984    }
24985
24986    if (parent.cmd.builder != "html") {
24987        cstring *arg = &ary_Alloc(args);
24988        *arg << "-builder:";
24989        Smallstr50_Print(parent.cmd.builder, *arg);
24990    }
24991
24992    if (parent.cmd.build_fail_on_warning != false) {
24993        cstring *arg = &ary_Alloc(args);
24994        *arg << "-build_fail_on_warning:";
24995        bool_Print(parent.cmd.build_fail_on_warning, *arg);
24996    }
24997
24998    if (parent.cmd.build_quiet != false) {
24999        cstring *arg = &ary_Alloc(args);
25000        *arg << "-build_quiet:";
25001        bool_Print(parent.cmd.build_quiet, *arg);
25002    }
25003
25004    if (parent.cmd.jobs != 0) {
25005        cstring *arg = &ary_Alloc(args);
25006        *arg << "-jobs:";
25007        u32_Print(parent.cmd.jobs, *arg);
25008    }
25009
25010    if (parent.cmd.preserve_footer != false) {
25011        cstring *arg = &ary_Alloc(args);
25012        *arg << "-preserve_footer:";
25013        bool_Print(parent.cmd.preserve_footer, *arg);
25014    }
25015
25016    if (parent.cmd.clean != false) {
25017        cstring *arg = &ary_Alloc(args);
25018        *arg << "-clean:";
25019        bool_Print(parent.cmd.clean, *arg);
25020    }
25021
25022    if (parent.cmd.serve != false) {
25023        cstring *arg = &ary_Alloc(args);
25024        *arg << "-serve:";
25025        bool_Print(parent.cmd.serve, *arg);
25026    }
25027
25028    if (parent.cmd.serve_dctrhost != "") {
25029        cstring *arg = &ary_Alloc(args);
25030        *arg << "-serve_dctrhost:";
25031        Smallstr50_Print(parent.cmd.serve_dctrhost, *arg);
25032    }
25033
25034    if (parent.cmd.serve_pages != false) {
25035        cstring *arg = &ary_Alloc(args);
25036        *arg << "-serve_pages:";
25037        bool_Print(parent.cmd.serve_pages, *arg);
25038    }
25039
25040    if (parent.cmd.serve_verify != false) {
25041        cstring *arg = &ary_Alloc(args);
25042        *arg << "-serve_verify:";
25043        bool_Print(parent.cmd.serve_verify, *arg);
25044    }
25045
25046    if (parent.cmd.workdir != "temp/") {
25047        cstring *arg = &ary_Alloc(args);
25048        *arg << "-workdir:";
25049        cstring_Print(parent.cmd.workdir, *arg);
25050    }
25051
25052    if (parent.cmd.dry_run != false) {
25053        cstring *arg = &ary_Alloc(args);
25054        *arg << "-dry_run:";
25055        bool_Print(parent.cmd.dry_run, *arg);
25056    }
25057
25058    if (parent.cmd.opts_numbered != false) {
25059        cstring *arg = &ary_Alloc(args);
25060        *arg << "-opts_numbered:";
25061        bool_Print(parent.cmd.opts_numbered, *arg);
25062    }
25063    for (int i=1; i < algo_lib::_db.cmdline.verbose; ++i) {
25064        ary_Alloc(args) << "-verbose";
25065    }
25066}
25067
25068// --- command.spnx_proc..Uninit
25069void command::spnx_proc_Uninit(command::spnx_proc& parent) {
25070    command::spnx_proc &row = parent; (void)row;
25071
25072    // command.spnx_proc.spnx.Uninit (Exec)  //
25073    spnx_Kill(parent); // kill child, ensure forward progress
25074}
25075
25076// --- command.src_func.target.Print
25077// Print back to string
25078void command::target_Print(command::src_func& parent, algo::cstring &out) {
25079    Regx_Print(parent.target, out);
25080}
25081
25082// --- command.src_func.target.ReadStrptrMaybe
25083// Read Regx from string
25084// Convert string to field. Return success value
25085bool command::target_ReadStrptrMaybe(command::src_func& parent, algo::strptr in) {
25086    bool retval = true;
25087    Regx_ReadSql(parent.target, in, true);
25088    return retval;
25089}
25090
25091// --- command.src_func.name.Print
25092// Print back to string
25093void command::name_Print(command::src_func& parent, algo::cstring &out) {
25094    Regx_Print(parent.name, out);
25095}
25096
25097// --- command.src_func.name.ReadStrptrMaybe
25098// Read Regx from string
25099// Convert string to field. Return success value
25100bool command::name_ReadStrptrMaybe(command::src_func& parent, algo::strptr in) {
25101    bool retval = true;
25102    Regx_ReadSql(parent.name, in, true);
25103    return retval;
25104}
25105
25106// --- command.src_func.body.Print
25107// Print back to string
25108void command::body_Print(command::src_func& parent, algo::cstring &out) {
25109    Regx_Print(parent.body, out);
25110}
25111
25112// --- command.src_func.body.ReadStrptrMaybe
25113// Read Regx from string
25114// Convert string to field. Return success value
25115bool command::body_ReadStrptrMaybe(command::src_func& parent, algo::strptr in) {
25116    bool retval = true;
25117    Regx_ReadSql(parent.body, in, true);
25118    return retval;
25119}
25120
25121// --- command.src_func.targsrc.Print
25122// Print back to string
25123void command::targsrc_Print(command::src_func& parent, algo::cstring &out) {
25124    Regx_Print(parent.targsrc, out);
25125}
25126
25127// --- command.src_func.targsrc.ReadStrptrMaybe
25128// Read Regx from string
25129// Convert string to field. Return success value
25130bool command::targsrc_ReadStrptrMaybe(command::src_func& parent, algo::strptr in) {
25131    bool retval = true;
25132    Regx_ReadSql(parent.targsrc, in, true);
25133    return retval;
25134}
25135
25136// --- command.src_func.func.Print
25137// Print back to string
25138void command::func_Print(command::src_func& parent, algo::cstring &out) {
25139    Regx_Print(parent.func, out);
25140}
25141
25142// --- command.src_func.func.ReadStrptrMaybe
25143// Read Regx from string
25144// Convert string to field. Return success value
25145bool command::func_ReadStrptrMaybe(command::src_func& parent, algo::strptr in) {
25146    bool retval = true;
25147    Regx_ReadSql(parent.func, in, true);
25148    return retval;
25149}
25150
25151// --- command.src_func.comment.Print
25152// Print back to string
25153void command::comment_Print(command::src_func& parent, algo::cstring &out) {
25154    Regx_Print(parent.comment, out);
25155}
25156
25157// --- command.src_func.comment.ReadStrptrMaybe
25158// Read Regx from string
25159// Convert string to field. Return success value
25160bool command::comment_ReadStrptrMaybe(command::src_func& parent, algo::strptr in) {
25161    bool retval = true;
25162    Regx_ReadSql(parent.comment, in, true);
25163    return retval;
25164}
25165
25166// --- command.src_func..ReadFieldMaybe
25167bool command::src_func_ReadFieldMaybe(command::src_func& parent, algo::strptr field, algo::strptr strval) {
25168    bool retval = true;
25169    command::FieldId field_id;
25170    (void)value_SetStrptrMaybe(field_id,field);
25171    switch(field_id) {
25172        case command_FieldId_in: {
25173            retval = algo::cstring_ReadStrptrMaybe(parent.in, strval);
25174            break;
25175        }
25176        case command_FieldId_target: {
25177            retval = target_ReadStrptrMaybe(parent, strval);
25178            break;
25179        }
25180        case command_FieldId_name: {
25181            retval = name_ReadStrptrMaybe(parent, strval);
25182            break;
25183        }
25184        case command_FieldId_body: {
25185            retval = body_ReadStrptrMaybe(parent, strval);
25186            break;
25187        }
25188        case command_FieldId_targsrc: {
25189            retval = targsrc_ReadStrptrMaybe(parent, strval);
25190            break;
25191        }
25192        case command_FieldId_func: {
25193            retval = func_ReadStrptrMaybe(parent, strval);
25194            break;
25195        }
25196        case command_FieldId_comment: {
25197            retval = comment_ReadStrptrMaybe(parent, strval);
25198            break;
25199        }
25200        case command_FieldId_nextfile: {
25201            retval = algo::Smallstr200_ReadStrptrMaybe(parent.nextfile, strval);
25202            break;
25203        }
25204        case command_FieldId_other: {
25205            retval = bool_ReadStrptrMaybe(parent.other, strval);
25206            break;
25207        }
25208        case command_FieldId_updateproto: {
25209            retval = bool_ReadStrptrMaybe(parent.updateproto, strval);
25210            break;
25211        }
25212        case command_FieldId_listfunc: {
25213            retval = bool_ReadStrptrMaybe(parent.listfunc, strval);
25214            break;
25215        }
25216        case command_FieldId_iffy: {
25217            retval = bool_ReadStrptrMaybe(parent.iffy, strval);
25218            break;
25219        }
25220        case command_FieldId_proto: {
25221            retval = bool_ReadStrptrMaybe(parent.proto, strval);
25222            break;
25223        }
25224        case command_FieldId_gen: {
25225            retval = bool_ReadStrptrMaybe(parent.gen, strval);
25226            break;
25227        }
25228        case command_FieldId_showloc: {
25229            retval = bool_ReadStrptrMaybe(parent.showloc, strval);
25230            break;
25231        }
25232        case command_FieldId_showstatic: {
25233            retval = bool_ReadStrptrMaybe(parent.showstatic, strval);
25234            break;
25235        }
25236        case command_FieldId_showsortkey: {
25237            retval = bool_ReadStrptrMaybe(parent.showsortkey, strval);
25238            break;
25239        }
25240        case command_FieldId_sortname: {
25241            retval = bool_ReadStrptrMaybe(parent.sortname, strval);
25242            break;
25243        }
25244        case command_FieldId_e: {
25245            retval = bool_ReadStrptrMaybe(parent.e, strval);
25246            break;
25247        }
25248        case command_FieldId_baddecl: {
25249            retval = bool_ReadStrptrMaybe(parent.baddecl, strval);
25250            break;
25251        }
25252        case command_FieldId_report: {
25253            retval = bool_ReadStrptrMaybe(parent.report, strval);
25254            break;
25255        }
25256        default: break;
25257    }
25258    if (!retval) {
25259        algo_lib::AppendErrtext("attr",field);
25260    }
25261    return retval;
25262}
25263
25264// --- command.src_func..ReadTupleMaybe
25265// Read fields of command::src_func from attributes of ascii tuple TUPLE
25266bool command::src_func_ReadTupleMaybe(command::src_func &parent, algo::Tuple &tuple) {
25267    bool retval = true;
25268    int anon_idx = 0;
25269    ind_beg(algo::Tuple_attrs_curs,attr,tuple) {
25270        if (ch_N(attr.name) == 0) {
25271            attr.name = src_func_GetAnon(parent, anon_idx++);
25272        }
25273        retval = src_func_ReadFieldMaybe(parent, attr.name, attr.value);
25274        if (!retval) {
25275            break;
25276        }
25277    }ind_end;
25278    return retval;
25279}
25280
25281// --- command.src_func..Init
25282// Set all fields to initial values.
25283void command::src_func_Init(command::src_func& parent) {
25284    parent.in = algo::strptr("data");
25285    Regx_ReadSql(parent.target, "%", true);
25286    Regx_ReadSql(parent.name, "%", true);
25287    Regx_ReadSql(parent.body, "%", true);
25288    Regx_ReadSql(parent.targsrc, "", true);
25289    Regx_ReadSql(parent.func, "%", true);
25290    Regx_ReadSql(parent.comment, "%", true);
25291    parent.nextfile = algo::strptr("");
25292    parent.other = bool(false);
25293    parent.updateproto = bool(false);
25294    parent.listfunc = bool(false);
25295    parent.iffy = bool(false);
25296    parent.proto = bool(false);
25297    parent.gen = bool(false);
25298    parent.showloc = bool(true);
25299    parent.showstatic = bool(true);
25300    parent.showsortkey = bool(false);
25301    parent.sortname = bool(false);
25302    parent.e = bool(false);
25303    parent.baddecl = bool(false);
25304    parent.report = bool(false);
25305}
25306
25307// --- command.src_func..ToCmdline
25308// Convenience function that returns a full command line
25309// Assume command is in a directory called bin
25310tempstr command::src_func_ToCmdline(command::src_func& row) {
25311    tempstr ret;
25312    ret << "bin/src_func ";
25313    src_func_PrintArgv(row, ret);
25314    // inherit less intense verbose, debug options
25315    for (int i = 1; i < algo_lib::_db.cmdline.verbose; i++) {
25316        ret << " -verbose";
25317    }
25318    for (int i = 1; i < algo_lib::_db.cmdline.debug; i++) {
25319        ret << " -debug";
25320    }
25321    return ret;
25322}
25323
25324// --- command.src_func..PrintArgv
25325// print string representation of ROW to string STR
25326// cfmt:command.src_func.Argv  printfmt:Tuple
25327void command::src_func_PrintArgv(command::src_func& row, algo::cstring& str) {
25328    algo::tempstr temp;
25329    (void)temp;
25330    (void)str;
25331    if (!(row.in == "data")) {
25332        ch_RemoveAll(temp);
25333        cstring_Print(row.in, temp);
25334        str << " -in:";
25335        strptr_PrintBash(temp,str);
25336    }
25337    ch_RemoveAll(temp);
25338    command::target_Print(const_cast<command::src_func&>(row), temp);
25339    str << " -target:";
25340    strptr_PrintBash(temp,str);
25341    ch_RemoveAll(temp);
25342    command::name_Print(const_cast<command::src_func&>(row), temp);
25343    str << " -name:";
25344    strptr_PrintBash(temp,str);
25345    ch_RemoveAll(temp);
25346    command::body_Print(const_cast<command::src_func&>(row), temp);
25347    str << " -body:";
25348    strptr_PrintBash(temp,str);
25349    if (!(row.targsrc.expr == "")) {
25350        ch_RemoveAll(temp);
25351        command::targsrc_Print(const_cast<command::src_func&>(row), temp);
25352        str << " -targsrc:";
25353        strptr_PrintBash(temp,str);
25354    }
25355    if (!(row.func.expr == "%")) {
25356        ch_RemoveAll(temp);
25357        command::func_Print(const_cast<command::src_func&>(row), temp);
25358        str << " -func:";
25359        strptr_PrintBash(temp,str);
25360    }
25361    if (!(row.comment.expr == "%")) {
25362        ch_RemoveAll(temp);
25363        command::comment_Print(const_cast<command::src_func&>(row), temp);
25364        str << " -comment:";
25365        strptr_PrintBash(temp,str);
25366    }
25367    if (!(row.nextfile == "")) {
25368        ch_RemoveAll(temp);
25369        Smallstr200_Print(row.nextfile, temp);
25370        str << " -nextfile:";
25371        strptr_PrintBash(temp,str);
25372    }
25373    if (!(row.other == false)) {
25374        ch_RemoveAll(temp);
25375        bool_Print(row.other, temp);
25376        str << " -other:";
25377        strptr_PrintBash(temp,str);
25378    }
25379    if (!(row.updateproto == false)) {
25380        ch_RemoveAll(temp);
25381        bool_Print(row.updateproto, temp);
25382        str << " -updateproto:";
25383        strptr_PrintBash(temp,str);
25384    }
25385    if (!(row.listfunc == false)) {
25386        ch_RemoveAll(temp);
25387        bool_Print(row.listfunc, temp);
25388        str << " -listfunc:";
25389        strptr_PrintBash(temp,str);
25390    }
25391    if (!(row.iffy == false)) {
25392        ch_RemoveAll(temp);
25393        bool_Print(row.iffy, temp);
25394        str << " -iffy:";
25395        strptr_PrintBash(temp,str);
25396    }
25397    if (!(row.proto == false)) {
25398        ch_RemoveAll(temp);
25399        bool_Print(row.proto, temp);
25400        str << " -proto:";
25401        strptr_PrintBash(temp,str);
25402    }
25403    if (!(row.gen == false)) {
25404        ch_RemoveAll(temp);
25405        bool_Print(row.gen, temp);
25406        str << " -gen:";
25407        strptr_PrintBash(temp,str);
25408    }
25409    if (!(row.showloc == true)) {
25410        ch_RemoveAll(temp);
25411        bool_Print(row.showloc, temp);
25412        str << " -showloc:";
25413        strptr_PrintBash(temp,str);
25414    }
25415    if (!(row.showstatic == true)) {
25416        ch_RemoveAll(temp);
25417        bool_Print(row.showstatic, temp);
25418        str << " -showstatic:";
25419        strptr_PrintBash(temp,str);
25420    }
25421    if (!(row.showsortkey == false)) {
25422        ch_RemoveAll(temp);
25423        bool_Print(row.showsortkey, temp);
25424        str << " -showsortkey:";
25425        strptr_PrintBash(temp,str);
25426    }
25427    if (!(row.sortname == false)) {
25428        ch_RemoveAll(temp);
25429        bool_Print(row.sortname, temp);
25430        str << " -sortname:";
25431        strptr_PrintBash(temp,str);
25432    }
25433    if (!(row.e == false)) {
25434        ch_RemoveAll(temp);
25435        bool_Print(row.e, temp);
25436        str << " -e:";
25437        strptr_PrintBash(temp,str);
25438    }
25439    if (!(row.baddecl == false)) {
25440        ch_RemoveAll(temp);
25441        bool_Print(row.baddecl, temp);
25442        str << " -baddecl:";
25443        strptr_PrintBash(temp,str);
25444    }
25445    if (!(row.report == false)) {
25446        ch_RemoveAll(temp);
25447        bool_Print(row.report, temp);
25448        str << " -report:";
25449        strptr_PrintBash(temp,str);
25450    }
25451}
25452
25453// --- command.src_func..GetAnon
25454algo::strptr command::src_func_GetAnon(command::src_func &parent, i32 idx) {
25455    (void)parent;//only to avoid -Wunused-parameter
25456    switch(idx) {
25457        case(0): return strptr("target", 6);
25458        case(1): return strptr("name", 4);
25459        case(2): return strptr("body", 4);
25460        default: return algo::strptr();
25461    }
25462}
25463
25464// --- command.src_func..NArgs
25465// Used with command lines
25466// Return # of command-line arguments that must follow this argument
25467// If FIELD is invalid, return -1
25468i32 command::src_func_NArgs(command::FieldId field, algo::strptr& out_dflt, bool* out_anon) {
25469    i32 retval = 1;
25470    switch (field) {
25471        case command_FieldId_in: { // $comment
25472            *out_anon = false;
25473        } break;
25474        case command_FieldId_target: { // $comment
25475            *out_anon = true;
25476        } break;
25477        case command_FieldId_name: { // $comment
25478            *out_anon = true;
25479        } break;
25480        case command_FieldId_body: { // $comment
25481            *out_anon = true;
25482        } break;
25483        case command_FieldId_targsrc: { // $comment
25484            *out_anon = false;
25485        } break;
25486        case command_FieldId_func: { // $comment
25487            *out_anon = false;
25488        } break;
25489        case command_FieldId_comment: { // $comment
25490            *out_anon = false;
25491        } break;
25492        case command_FieldId_nextfile: { // $comment
25493            *out_anon = false;
25494        } break;
25495        case command_FieldId_other: { // $comment
25496            *out_anon = false;
25497            retval=0;
25498            out_dflt="Y";
25499        } break;
25500        case command_FieldId_updateproto: { // bool: no argument required but value may be specified as other:Y
25501            *out_anon = false;
25502            retval=0;
25503            out_dflt="Y";
25504        } break;
25505        case command_FieldId_listfunc: { // bool: no argument required but value may be specified as updateproto:Y
25506            *out_anon = false;
25507            retval=0;
25508            out_dflt="Y";
25509        } break;
25510        case command_FieldId_iffy: { // bool: no argument required but value may be specified as listfunc:Y
25511            *out_anon = false;
25512            retval=0;
25513            out_dflt="Y";
25514        } break;
25515        case command_FieldId_proto: { // bool: no argument required but value may be specified as iffy:Y
25516            *out_anon = false;
25517            retval=0;
25518            out_dflt="Y";
25519        } break;
25520        case command_FieldId_gen: { // bool: no argument required but value may be specified as proto:Y
25521            *out_anon = false;
25522            retval=0;
25523            out_dflt="Y";
25524        } break;
25525        case command_FieldId_showloc: { // bool: no argument required but value may be specified as gen:Y
25526            *out_anon = false;
25527            retval=0;
25528            out_dflt="Y";
25529        } break;
25530        case command_FieldId_showstatic: { // bool: no argument required but value may be specified as showloc:Y
25531            *out_anon = false;
25532            retval=0;
25533            out_dflt="Y";
25534        } break;
25535        case command_FieldId_showsortkey: { // bool: no argument required but value may be specified as showstatic:Y
25536            *out_anon = false;
25537            retval=0;
25538            out_dflt="Y";
25539        } break;
25540        case command_FieldId_sortname: { // bool: no argument required but value may be specified as showsortkey:Y
25541            *out_anon = false;
25542            retval=0;
25543            out_dflt="Y";
25544        } break;
25545        case command_FieldId_e: { // bool: no argument required but value may be specified as sortname:Y
25546            *out_anon = false;
25547            retval=0;
25548            out_dflt="Y";
25549        } break;
25550        case command_FieldId_baddecl: { // bool: no argument required but value may be specified as e:Y
25551            *out_anon = false;
25552            retval=0;
25553            out_dflt="Y";
25554        } break;
25555        case command_FieldId_report: { // bool: no argument required but value may be specified as baddecl:Y
25556            *out_anon = false;
25557            retval=0;
25558            out_dflt="Y";
25559        } break;
25560        default:
25561        retval=-1; // unrecognized
25562    }
25563    return retval;
25564}
25565
25566// --- command.src_func_proc.src_func.Start
25567// Start subprocess
25568// If subprocess already running, do nothing. Otherwise, start it
25569int command::src_func_Start(command::src_func_proc& parent) {
25570    int retval = 0;
25571    if (parent.pid == 0) {
25572        verblog(src_func_ToCmdline(parent)); // maybe print command
25573#ifdef WIN32
25574        algo_lib::ResolveExecFname(parent.path);
25575        tempstr cmdline(src_func_ToCmdline(parent));
25576        parent.pid = dospawn(Zeroterm(parent.path),Zeroterm(cmdline),parent.timeout,parent.fstdin,parent.fstdout,parent.fstderr);
25577#else
25578        parent.pid = fork();
25579        if (parent.pid == 0) { // child
25580            algo_lib::DieWithParent();
25581            if (parent.timeout > 0) {
25582                alarm(parent.timeout);
25583            }
25584            if (retval==0) retval=algo_lib::ApplyRedirect(parent.fstdin , 0);
25585            if (retval==0) retval=algo_lib::ApplyRedirect(parent.fstdout, 1);
25586            if (retval==0) retval=algo_lib::ApplyRedirect(parent.fstderr, 2);
25587            if (retval==0) retval= src_func_Execv(parent);
25588            if (retval != 0) { // if start fails, print error
25589                int err=errno;
25590                prerr("command.src_func_execv"
25591                <<Keyval("errno",err)
25592                <<Keyval("errstr",strerror(err))
25593                <<Keyval("comment","Execv failed"));
25594            }
25595            _exit(127); // if failed to start, exit anyway
25596        } else if (parent.pid == -1) {
25597            retval = errno; // failed to fork
25598        }
25599#endif
25600    }
25601    parent.status = parent.pid > 0 ? 0 : -1; // if didn't start, set error status
25602    return retval;
25603}
25604
25605// --- command.src_func_proc.src_func.StartRead
25606// Start subprocess & Read output
25607algo::Fildes command::src_func_StartRead(command::src_func_proc& parent, algo_lib::FFildes &read) {
25608    int pipefd[2];
25609    int rc=pipe(pipefd);
25610    (void)rc;
25611    read.fd.value = pipefd[0];
25612    parent.fstdout  << ">&" << pipefd[1];
25613    src_func_Start(parent);
25614    (void)close(pipefd[1]);
25615    return read.fd;
25616}
25617
25618// --- command.src_func_proc.src_func.Kill
25619// Kill subprocess and wait
25620void command::src_func_Kill(command::src_func_proc& parent) {
25621    if (parent.pid != 0) {
25622        kill(parent.pid,9);
25623        src_func_Wait(parent);
25624    }
25625}
25626
25627// --- command.src_func_proc.src_func.Wait
25628// Wait for subprocess to return
25629void command::src_func_Wait(command::src_func_proc& parent) {
25630    if (parent.pid > 0) {
25631        int wait_flags = 0;
25632        int wait_status = 0;
25633        int rc = -1;
25634        do {
25635            // really wait for subprocess to exit
25636            rc = waitpid(parent.pid,&wait_status,wait_flags);
25637        } while (rc==-1 && errno==EINTR);
25638        if (rc == parent.pid) {
25639            parent.status = wait_status;
25640            parent.pid = 0;
25641        }
25642    }
25643}
25644
25645// --- command.src_func_proc.src_func.Exec
25646// Start + Wait
25647// Execute subprocess and return exit code
25648int command::src_func_Exec(command::src_func_proc& parent) {
25649    src_func_Start(parent);
25650    src_func_Wait(parent);
25651    return parent.status;
25652}
25653
25654// --- command.src_func_proc.src_func.ExecX
25655// Start + Wait, throw exception on error
25656// Execute subprocess; throw human-readable exception on error
25657void command::src_func_ExecX(command::src_func_proc& parent) {
25658    int rc = src_func_Exec(parent);
25659    vrfy(rc==0, tempstr() << "algo_lib.exec" << Keyval("cmd",src_func_ToCmdline(parent))
25660    << Keyval("comment",algo::DescribeWaitStatus(parent.status)));
25661}
25662
25663// --- command.src_func_proc.src_func.Execv
25664// Call execv()
25665// Call execv with specified parameters
25666int command::src_func_Execv(command::src_func_proc& parent) {
25667    int ret = 0;
25668    algo::StringAry args;
25669    src_func_ToArgv(parent, args);
25670    char **argv = (char**)alloca((ary_N(args)+1)*sizeof(*argv));
25671    ind_beg(algo::StringAry_ary_curs,arg,args) {
25672        argv[ind_curs(arg).index] = Zeroterm(arg);
25673    }ind_end;
25674    argv[ary_N(args)] = NULL;
25675    // if parent.path is relative, search for it in PATH
25676    algo_lib::ResolveExecFname(parent.path);
25677    ret = execv(Zeroterm(parent.path),argv);
25678    return ret;
25679}
25680
25681// --- command.src_func_proc.src_func.ToCmdline
25682algo::tempstr command::src_func_ToCmdline(command::src_func_proc& parent) {
25683    algo::tempstr retval;
25684    retval << parent.path << " ";
25685    command::src_func_PrintArgv(parent.cmd,retval);
25686    if (ch_N(parent.fstdin)) {
25687        retval << " " << parent.fstdin;
25688    }
25689    if (ch_N(parent.fstdout)) {
25690        retval << " " << parent.fstdout;
25691    }
25692    if (ch_N(parent.fstderr)) {
25693        retval << " 2" << parent.fstderr;
25694    }
25695    return retval;
25696}
25697
25698// --- command.src_func_proc.src_func.ToArgv
25699// Form array from the command line
25700void command::src_func_ToArgv(command::src_func_proc& parent, algo::StringAry& args) {
25701    ary_RemoveAll(args);
25702    ary_Alloc(args) << parent.path;
25703
25704    if (parent.cmd.in != "data") {
25705        cstring *arg = &ary_Alloc(args);
25706        *arg << "-in:";
25707        cstring_Print(parent.cmd.in, *arg);
25708    }
25709
25710    if (parent.cmd.target.expr != "%") {
25711        cstring *arg = &ary_Alloc(args);
25712        *arg << "-target:";
25713        command::target_Print(parent.cmd, *arg);
25714    }
25715
25716    if (parent.cmd.name.expr != "%") {
25717        cstring *arg = &ary_Alloc(args);
25718        *arg << "-name:";
25719        command::name_Print(parent.cmd, *arg);
25720    }
25721
25722    if (parent.cmd.body.expr != "%") {
25723        cstring *arg = &ary_Alloc(args);
25724        *arg << "-body:";
25725        command::body_Print(parent.cmd, *arg);
25726    }
25727
25728    if (parent.cmd.targsrc.expr != "") {
25729        cstring *arg = &ary_Alloc(args);
25730        *arg << "-targsrc:";
25731        command::targsrc_Print(parent.cmd, *arg);
25732    }
25733
25734    if (parent.cmd.func.expr != "%") {
25735        cstring *arg = &ary_Alloc(args);
25736        *arg << "-func:";
25737        command::func_Print(parent.cmd, *arg);
25738    }
25739
25740    if (parent.cmd.comment.expr != "%") {
25741        cstring *arg = &ary_Alloc(args);
25742        *arg << "-comment:";
25743        command::comment_Print(parent.cmd, *arg);
25744    }
25745
25746    if (parent.cmd.nextfile != "") {
25747        cstring *arg = &ary_Alloc(args);
25748        *arg << "-nextfile:";
25749        Smallstr200_Print(parent.cmd.nextfile, *arg);
25750    }
25751
25752    if (parent.cmd.other != false) {
25753        cstring *arg = &ary_Alloc(args);
25754        *arg << "-other:";
25755        bool_Print(parent.cmd.other, *arg);
25756    }
25757
25758    if (parent.cmd.updateproto != false) {
25759        cstring *arg = &ary_Alloc(args);
25760        *arg << "-updateproto:";
25761        bool_Print(parent.cmd.updateproto, *arg);
25762    }
25763
25764    if (parent.cmd.listfunc != false) {
25765        cstring *arg = &ary_Alloc(args);
25766        *arg << "-listfunc:";
25767        bool_Print(parent.cmd.listfunc, *arg);
25768    }
25769
25770    if (parent.cmd.iffy != false) {
25771        cstring *arg = &ary_Alloc(args);
25772        *arg << "-iffy:";
25773        bool_Print(parent.cmd.iffy, *arg);
25774    }
25775
25776    if (parent.cmd.proto != false) {
25777        cstring *arg = &ary_Alloc(args);
25778        *arg << "-proto:";
25779        bool_Print(parent.cmd.proto, *arg);
25780    }
25781
25782    if (parent.cmd.gen != false) {
25783        cstring *arg = &ary_Alloc(args);
25784        *arg << "-gen:";
25785        bool_Print(parent.cmd.gen, *arg);
25786    }
25787
25788    if (parent.cmd.showloc != true) {
25789        cstring *arg = &ary_Alloc(args);
25790        *arg << "-showloc:";
25791        bool_Print(parent.cmd.showloc, *arg);
25792    }
25793
25794    if (parent.cmd.showstatic != true) {
25795        cstring *arg = &ary_Alloc(args);
25796        *arg << "-showstatic:";
25797        bool_Print(parent.cmd.showstatic, *arg);
25798    }
25799
25800    if (parent.cmd.showsortkey != false) {
25801        cstring *arg = &ary_Alloc(args);
25802        *arg << "-showsortkey:";
25803        bool_Print(parent.cmd.showsortkey, *arg);
25804    }
25805
25806    if (parent.cmd.sortname != false) {
25807        cstring *arg = &ary_Alloc(args);
25808        *arg << "-sortname:";
25809        bool_Print(parent.cmd.sortname, *arg);
25810    }
25811
25812    if (parent.cmd.e != false) {
25813        cstring *arg = &ary_Alloc(args);
25814        *arg << "-e:";
25815        bool_Print(parent.cmd.e, *arg);
25816    }
25817
25818    if (parent.cmd.baddecl != false) {
25819        cstring *arg = &ary_Alloc(args);
25820        *arg << "-baddecl:";
25821        bool_Print(parent.cmd.baddecl, *arg);
25822    }
25823
25824    if (parent.cmd.report != false) {
25825        cstring *arg = &ary_Alloc(args);
25826        *arg << "-report:";
25827        bool_Print(parent.cmd.report, *arg);
25828    }
25829    for (int i=1; i < algo_lib::_db.cmdline.verbose; ++i) {
25830        ary_Alloc(args) << "-verbose";
25831    }
25832}
25833
25834// --- command.src_func_proc..Uninit
25835void command::src_func_proc_Uninit(command::src_func_proc& parent) {
25836    command::src_func_proc &row = parent; (void)row;
25837
25838    // command.src_func_proc.src_func.Uninit (Exec)  //
25839    src_func_Kill(parent); // kill child, ensure forward progress
25840}
25841
25842// --- command.src_hdr.targsrc.Print
25843// Print back to string
25844void command::targsrc_Print(command::src_hdr& parent, algo::cstring &out) {
25845    Regx_Print(parent.targsrc, out);
25846}
25847
25848// --- command.src_hdr.targsrc.ReadStrptrMaybe
25849// Read Regx from string
25850// Convert string to field. Return success value
25851bool command::targsrc_ReadStrptrMaybe(command::src_hdr& parent, algo::strptr in) {
25852    bool retval = true;
25853    Regx_ReadSql(parent.targsrc, in, true);
25854    return retval;
25855}
25856
25857// --- command.src_hdr.scriptfile.Print
25858// Print back to string
25859void command::scriptfile_Print(command::src_hdr& parent, algo::cstring &out) {
25860    Regx_Print(parent.scriptfile, out);
25861}
25862
25863// --- command.src_hdr.scriptfile.ReadStrptrMaybe
25864// Read Regx from string
25865// Convert string to field. Return success value
25866bool command::scriptfile_ReadStrptrMaybe(command::src_hdr& parent, algo::strptr in) {
25867    bool retval = true;
25868    Regx_ReadSql(parent.scriptfile, in, true);
25869    return retval;
25870}
25871
25872// --- command.src_hdr..ReadFieldMaybe
25873bool command::src_hdr_ReadFieldMaybe(command::src_hdr& parent, algo::strptr field, algo::strptr strval) {
25874    bool retval = true;
25875    command::FieldId field_id;
25876    (void)value_SetStrptrMaybe(field_id,field);
25877    switch(field_id) {
25878        case command_FieldId_in: {
25879            retval = algo::cstring_ReadStrptrMaybe(parent.in, strval);
25880            break;
25881        }
25882        case command_FieldId_targsrc: {
25883            retval = targsrc_ReadStrptrMaybe(parent, strval);
25884            break;
25885        }
25886        case command_FieldId_write: {
25887            retval = bool_ReadStrptrMaybe(parent.write, strval);
25888            break;
25889        }
25890        case command_FieldId_indent: {
25891            retval = bool_ReadStrptrMaybe(parent.indent, strval);
25892            break;
25893        }
25894        case command_FieldId_update_copyright: {
25895            retval = bool_ReadStrptrMaybe(parent.update_copyright, strval);
25896            break;
25897        }
25898        case command_FieldId_scriptfile: {
25899            retval = scriptfile_ReadStrptrMaybe(parent, strval);
25900            break;
25901        }
25902        default: break;
25903    }
25904    if (!retval) {
25905        algo_lib::AppendErrtext("attr",field);
25906    }
25907    return retval;
25908}
25909
25910// --- command.src_hdr..ReadTupleMaybe
25911// Read fields of command::src_hdr from attributes of ascii tuple TUPLE
25912bool command::src_hdr_ReadTupleMaybe(command::src_hdr &parent, algo::Tuple &tuple) {
25913    bool retval = true;
25914    ind_beg(algo::Tuple_attrs_curs,attr,tuple) {
25915        retval = src_hdr_ReadFieldMaybe(parent, attr.name, attr.value);
25916        if (!retval) {
25917            break;
25918        }
25919    }ind_end;
25920    return retval;
25921}
25922
25923// --- command.src_hdr..Init
25924// Set all fields to initial values.
25925void command::src_hdr_Init(command::src_hdr& parent) {
25926    parent.in = algo::strptr("data");
25927    Regx_ReadSql(parent.targsrc, "", true);
25928    parent.write = bool(false);
25929    parent.indent = bool(false);
25930    parent.update_copyright = bool(false);
25931    Regx_ReadSql(parent.scriptfile, "", true);
25932}
25933
25934// --- command.src_hdr..ToCmdline
25935// Convenience function that returns a full command line
25936// Assume command is in a directory called bin
25937tempstr command::src_hdr_ToCmdline(command::src_hdr& row) {
25938    tempstr ret;
25939    ret << "bin/src_hdr ";
25940    src_hdr_PrintArgv(row, ret);
25941    // inherit less intense verbose, debug options
25942    for (int i = 1; i < algo_lib::_db.cmdline.verbose; i++) {
25943        ret << " -verbose";
25944    }
25945    for (int i = 1; i < algo_lib::_db.cmdline.debug; i++) {
25946        ret << " -debug";
25947    }
25948    return ret;
25949}
25950
25951// --- command.src_hdr..PrintArgv
25952// print string representation of ROW to string STR
25953// cfmt:command.src_hdr.Argv  printfmt:Tuple
25954void command::src_hdr_PrintArgv(command::src_hdr& row, algo::cstring& str) {
25955    algo::tempstr temp;
25956    (void)temp;
25957    (void)str;
25958    if (!(row.in == "data")) {
25959        ch_RemoveAll(temp);
25960        cstring_Print(row.in, temp);
25961        str << " -in:";
25962        strptr_PrintBash(temp,str);
25963    }
25964    if (!(row.targsrc.expr == "")) {
25965        ch_RemoveAll(temp);
25966        command::targsrc_Print(const_cast<command::src_hdr&>(row), temp);
25967        str << " -targsrc:";
25968        strptr_PrintBash(temp,str);
25969    }
25970    if (!(row.write == false)) {
25971        ch_RemoveAll(temp);
25972        bool_Print(row.write, temp);
25973        str << " -write:";
25974        strptr_PrintBash(temp,str);
25975    }
25976    if (!(row.indent == false)) {
25977        ch_RemoveAll(temp);
25978        bool_Print(row.indent, temp);
25979        str << " -indent:";
25980        strptr_PrintBash(temp,str);
25981    }
25982    if (!(row.update_copyright == false)) {
25983        ch_RemoveAll(temp);
25984        bool_Print(row.update_copyright, temp);
25985        str << " -update_copyright:";
25986        strptr_PrintBash(temp,str);
25987    }
25988    if (!(row.scriptfile.expr == "")) {
25989        ch_RemoveAll(temp);
25990        command::scriptfile_Print(const_cast<command::src_hdr&>(row), temp);
25991        str << " -scriptfile:";
25992        strptr_PrintBash(temp,str);
25993    }
25994}
25995
25996// --- command.src_hdr..NArgs
25997// Used with command lines
25998// Return # of command-line arguments that must follow this argument
25999// If FIELD is invalid, return -1
26000i32 command::src_hdr_NArgs(command::FieldId field, algo::strptr& out_dflt, bool* out_anon) {
26001    i32 retval = 1;
26002    switch (field) {
26003        case command_FieldId_in: { // $comment
26004            *out_anon = false;
26005        } break;
26006        case command_FieldId_targsrc: { // $comment
26007            *out_anon = false;
26008        } break;
26009        case command_FieldId_write: { // $comment
26010            *out_anon = false;
26011            retval=0;
26012            out_dflt="Y";
26013        } break;
26014        case command_FieldId_indent: { // bool: no argument required but value may be specified as write:Y
26015            *out_anon = false;
26016            retval=0;
26017            out_dflt="Y";
26018        } break;
26019        case command_FieldId_update_copyright: { // bool: no argument required but value may be specified as indent:Y
26020            *out_anon = false;
26021            retval=0;
26022            out_dflt="Y";
26023        } break;
26024        case command_FieldId_scriptfile: { // bool: no argument required but value may be specified as update_copyright:Y
26025            *out_anon = false;
26026        } break;
26027        default:
26028        retval=-1; // unrecognized
26029    }
26030    return retval;
26031}
26032
26033// --- command.src_hdr_proc.src_hdr.Start
26034// Start subprocess
26035// If subprocess already running, do nothing. Otherwise, start it
26036int command::src_hdr_Start(command::src_hdr_proc& parent) {
26037    int retval = 0;
26038    if (parent.pid == 0) {
26039        verblog(src_hdr_ToCmdline(parent)); // maybe print command
26040#ifdef WIN32
26041        algo_lib::ResolveExecFname(parent.path);
26042        tempstr cmdline(src_hdr_ToCmdline(parent));
26043        parent.pid = dospawn(Zeroterm(parent.path),Zeroterm(cmdline),parent.timeout,parent.fstdin,parent.fstdout,parent.fstderr);
26044#else
26045        parent.pid = fork();
26046        if (parent.pid == 0) { // child
26047            algo_lib::DieWithParent();
26048            if (parent.timeout > 0) {
26049                alarm(parent.timeout);
26050            }
26051            if (retval==0) retval=algo_lib::ApplyRedirect(parent.fstdin , 0);
26052            if (retval==0) retval=algo_lib::ApplyRedirect(parent.fstdout, 1);
26053            if (retval==0) retval=algo_lib::ApplyRedirect(parent.fstderr, 2);
26054            if (retval==0) retval= src_hdr_Execv(parent);
26055            if (retval != 0) { // if start fails, print error
26056                int err=errno;
26057                prerr("command.src_hdr_execv"
26058                <<Keyval("errno",err)
26059                <<Keyval("errstr",strerror(err))
26060                <<Keyval("comment","Execv failed"));
26061            }
26062            _exit(127); // if failed to start, exit anyway
26063        } else if (parent.pid == -1) {
26064            retval = errno; // failed to fork
26065        }
26066#endif
26067    }
26068    parent.status = parent.pid > 0 ? 0 : -1; // if didn't start, set error status
26069    return retval;
26070}
26071
26072// --- command.src_hdr_proc.src_hdr.StartRead
26073// Start subprocess & Read output
26074algo::Fildes command::src_hdr_StartRead(command::src_hdr_proc& parent, algo_lib::FFildes &read) {
26075    int pipefd[2];
26076    int rc=pipe(pipefd);
26077    (void)rc;
26078    read.fd.value = pipefd[0];
26079    parent.fstdout  << ">&" << pipefd[1];
26080    src_hdr_Start(parent);
26081    (void)close(pipefd[1]);
26082    return read.fd;
26083}
26084
26085// --- command.src_hdr_proc.src_hdr.Kill
26086// Kill subprocess and wait
26087void command::src_hdr_Kill(command::src_hdr_proc& parent) {
26088    if (parent.pid != 0) {
26089        kill(parent.pid,9);
26090        src_hdr_Wait(parent);
26091    }
26092}
26093
26094// --- command.src_hdr_proc.src_hdr.Wait
26095// Wait for subprocess to return
26096void command::src_hdr_Wait(command::src_hdr_proc& parent) {
26097    if (parent.pid > 0) {
26098        int wait_flags = 0;
26099        int wait_status = 0;
26100        int rc = -1;
26101        do {
26102            // really wait for subprocess to exit
26103            rc = waitpid(parent.pid,&wait_status,wait_flags);
26104        } while (rc==-1 && errno==EINTR);
26105        if (rc == parent.pid) {
26106            parent.status = wait_status;
26107            parent.pid = 0;
26108        }
26109    }
26110}
26111
26112// --- command.src_hdr_proc.src_hdr.Exec
26113// Start + Wait
26114// Execute subprocess and return exit code
26115int command::src_hdr_Exec(command::src_hdr_proc& parent) {
26116    src_hdr_Start(parent);
26117    src_hdr_Wait(parent);
26118    return parent.status;
26119}
26120
26121// --- command.src_hdr_proc.src_hdr.ExecX
26122// Start + Wait, throw exception on error
26123// Execute subprocess; throw human-readable exception on error
26124void command::src_hdr_ExecX(command::src_hdr_proc& parent) {
26125    int rc = src_hdr_Exec(parent);
26126    vrfy(rc==0, tempstr() << "algo_lib.exec" << Keyval("cmd",src_hdr_ToCmdline(parent))
26127    << Keyval("comment",algo::DescribeWaitStatus(parent.status)));
26128}
26129
26130// --- command.src_hdr_proc.src_hdr.Execv
26131// Call execv()
26132// Call execv with specified parameters
26133int command::src_hdr_Execv(command::src_hdr_proc& parent) {
26134    int ret = 0;
26135    algo::StringAry args;
26136    src_hdr_ToArgv(parent, args);
26137    char **argv = (char**)alloca((ary_N(args)+1)*sizeof(*argv));
26138    ind_beg(algo::StringAry_ary_curs,arg,args) {
26139        argv[ind_curs(arg).index] = Zeroterm(arg);
26140    }ind_end;
26141    argv[ary_N(args)] = NULL;
26142    // if parent.path is relative, search for it in PATH
26143    algo_lib::ResolveExecFname(parent.path);
26144    ret = execv(Zeroterm(parent.path),argv);
26145    return ret;
26146}
26147
26148// --- command.src_hdr_proc.src_hdr.ToCmdline
26149algo::tempstr command::src_hdr_ToCmdline(command::src_hdr_proc& parent) {
26150    algo::tempstr retval;
26151    retval << parent.path << " ";
26152    command::src_hdr_PrintArgv(parent.cmd,retval);
26153    if (ch_N(parent.fstdin)) {
26154        retval << " " << parent.fstdin;
26155    }
26156    if (ch_N(parent.fstdout)) {
26157        retval << " " << parent.fstdout;
26158    }
26159    if (ch_N(parent.fstderr)) {
26160        retval << " 2" << parent.fstderr;
26161    }
26162    return retval;
26163}
26164
26165// --- command.src_hdr_proc.src_hdr.ToArgv
26166// Form array from the command line
26167void command::src_hdr_ToArgv(command::src_hdr_proc& parent, algo::StringAry& args) {
26168    ary_RemoveAll(args);
26169    ary_Alloc(args) << parent.path;
26170
26171    if (parent.cmd.in != "data") {
26172        cstring *arg = &ary_Alloc(args);
26173        *arg << "-in:";
26174        cstring_Print(parent.cmd.in, *arg);
26175    }
26176
26177    if (parent.cmd.targsrc.expr != "") {
26178        cstring *arg = &ary_Alloc(args);
26179        *arg << "-targsrc:";
26180        command::targsrc_Print(parent.cmd, *arg);
26181    }
26182
26183    if (parent.cmd.write != false) {
26184        cstring *arg = &ary_Alloc(args);
26185        *arg << "-write:";
26186        bool_Print(parent.cmd.write, *arg);
26187    }
26188
26189    if (parent.cmd.indent != false) {
26190        cstring *arg = &ary_Alloc(args);
26191        *arg << "-indent:";
26192        bool_Print(parent.cmd.indent, *arg);
26193    }
26194
26195    if (parent.cmd.update_copyright != false) {
26196        cstring *arg = &ary_Alloc(args);
26197        *arg << "-update_copyright:";
26198        bool_Print(parent.cmd.update_copyright, *arg);
26199    }
26200
26201    if (parent.cmd.scriptfile.expr != "") {
26202        cstring *arg = &ary_Alloc(args);
26203        *arg << "-scriptfile:";
26204        command::scriptfile_Print(parent.cmd, *arg);
26205    }
26206    for (int i=1; i < algo_lib::_db.cmdline.verbose; ++i) {
26207        ary_Alloc(args) << "-verbose";
26208    }
26209}
26210
26211// --- command.src_hdr_proc..Uninit
26212void command::src_hdr_proc_Uninit(command::src_hdr_proc& parent) {
26213    command::src_hdr_proc &row = parent; (void)row;
26214
26215    // command.src_hdr_proc.src_hdr.Uninit (Exec)  //
26216    src_hdr_Kill(parent); // kill child, ensure forward progress
26217}
26218
26219// --- command.src_lim.srcfile.Print
26220// Print back to string
26221void command::srcfile_Print(command::src_lim& parent, algo::cstring &out) {
26222    Regx_Print(parent.srcfile, out);
26223}
26224
26225// --- command.src_lim.srcfile.ReadStrptrMaybe
26226// Read Regx from string
26227// Convert string to field. Return success value
26228bool command::srcfile_ReadStrptrMaybe(command::src_lim& parent, algo::strptr in) {
26229    bool retval = true;
26230    Regx_ReadSql(parent.srcfile, in, true);
26231    return retval;
26232}
26233
26234// --- command.src_lim.badline.Print
26235// Print back to string
26236void command::badline_Print(command::src_lim& parent, algo::cstring &out) {
26237    Regx_Print(parent.badline, out);
26238}
26239
26240// --- command.src_lim.badline.ReadStrptrMaybe
26241// Read Regx from string
26242// Convert string to field. Return success value
26243bool command::badline_ReadStrptrMaybe(command::src_lim& parent, algo::strptr in) {
26244    bool retval = true;
26245    Regx_ReadSql(parent.badline, in, true);
26246    return retval;
26247}
26248
26249// --- command.src_lim..ReadFieldMaybe
26250bool command::src_lim_ReadFieldMaybe(command::src_lim& parent, algo::strptr field, algo::strptr strval) {
26251    bool retval = true;
26252    command::FieldId field_id;
26253    (void)value_SetStrptrMaybe(field_id,field);
26254    switch(field_id) {
26255        case command_FieldId_in: {
26256            retval = algo::cstring_ReadStrptrMaybe(parent.in, strval);
26257            break;
26258        }
26259        case command_FieldId_linelim: {
26260            retval = bool_ReadStrptrMaybe(parent.linelim, strval);
26261            break;
26262        }
26263        case command_FieldId_srcfile: {
26264            retval = srcfile_ReadStrptrMaybe(parent, strval);
26265            break;
26266        }
26267        case command_FieldId_strayfile: {
26268            retval = bool_ReadStrptrMaybe(parent.strayfile, strval);
26269            break;
26270        }
26271        case command_FieldId_capture: {
26272            retval = bool_ReadStrptrMaybe(parent.capture, strval);
26273            break;
26274        }
26275        case command_FieldId_write: {
26276            retval = bool_ReadStrptrMaybe(parent.write, strval);
26277            break;
26278        }
26279        case command_FieldId_badchar: {
26280            retval = bool_ReadStrptrMaybe(parent.badchar, strval);
26281            break;
26282        }
26283        case command_FieldId_badline: {
26284            retval = badline_ReadStrptrMaybe(parent, strval);
26285            break;
26286        }
26287        default: break;
26288    }
26289    if (!retval) {
26290        algo_lib::AppendErrtext("attr",field);
26291    }
26292    return retval;
26293}
26294
26295// --- command.src_lim..ReadTupleMaybe
26296// Read fields of command::src_lim from attributes of ascii tuple TUPLE
26297bool command::src_lim_ReadTupleMaybe(command::src_lim &parent, algo::Tuple &tuple) {
26298    bool retval = true;
26299    ind_beg(algo::Tuple_attrs_curs,attr,tuple) {
26300        retval = src_lim_ReadFieldMaybe(parent, attr.name, attr.value);
26301        if (!retval) {
26302            break;
26303        }
26304    }ind_end;
26305    return retval;
26306}
26307
26308// --- command.src_lim..Init
26309// Set all fields to initial values.
26310void command::src_lim_Init(command::src_lim& parent) {
26311    parent.in = algo::strptr("data");
26312    parent.linelim = bool(false);
26313    Regx_ReadSql(parent.srcfile, "%", true);
26314    parent.strayfile = bool(false);
26315    parent.capture = bool(false);
26316    parent.write = bool(false);
26317    parent.badchar = bool(false);
26318    Regx_ReadSql(parent.badline, "", true);
26319}
26320
26321// --- command.src_lim..ToCmdline
26322// Convenience function that returns a full command line
26323// Assume command is in a directory called bin
26324tempstr command::src_lim_ToCmdline(command::src_lim& row) {
26325    tempstr ret;
26326    ret << "bin/src_lim ";
26327    src_lim_PrintArgv(row, ret);
26328    // inherit less intense verbose, debug options
26329    for (int i = 1; i < algo_lib::_db.cmdline.verbose; i++) {
26330        ret << " -verbose";
26331    }
26332    for (int i = 1; i < algo_lib::_db.cmdline.debug; i++) {
26333        ret << " -debug";
26334    }
26335    return ret;
26336}
26337
26338// --- command.src_lim..PrintArgv
26339// print string representation of ROW to string STR
26340// cfmt:command.src_lim.Argv  printfmt:Tuple
26341void command::src_lim_PrintArgv(command::src_lim& row, algo::cstring& str) {
26342    algo::tempstr temp;
26343    (void)temp;
26344    (void)str;
26345    if (!(row.in == "data")) {
26346        ch_RemoveAll(temp);
26347        cstring_Print(row.in, temp);
26348        str << " -in:";
26349        strptr_PrintBash(temp,str);
26350    }
26351    if (!(row.linelim == false)) {
26352        ch_RemoveAll(temp);
26353        bool_Print(row.linelim, temp);
26354        str << " -linelim:";
26355        strptr_PrintBash(temp,str);
26356    }
26357    if (!(row.srcfile.expr == "%")) {
26358        ch_RemoveAll(temp);
26359        command::srcfile_Print(const_cast<command::src_lim&>(row), temp);
26360        str << " -srcfile:";
26361        strptr_PrintBash(temp,str);
26362    }
26363    if (!(row.strayfile == false)) {
26364        ch_RemoveAll(temp);
26365        bool_Print(row.strayfile, temp);
26366        str << " -strayfile:";
26367        strptr_PrintBash(temp,str);
26368    }
26369    if (!(row.capture == false)) {
26370        ch_RemoveAll(temp);
26371        bool_Print(row.capture, temp);
26372        str << " -capture:";
26373        strptr_PrintBash(temp,str);
26374    }
26375    if (!(row.write == false)) {
26376        ch_RemoveAll(temp);
26377        bool_Print(row.write, temp);
26378        str << " -write:";
26379        strptr_PrintBash(temp,str);
26380    }
26381    if (!(row.badchar == false)) {
26382        ch_RemoveAll(temp);
26383        bool_Print(row.badchar, temp);
26384        str << " -badchar:";
26385        strptr_PrintBash(temp,str);
26386    }
26387    if (!(row.badline.expr == "")) {
26388        ch_RemoveAll(temp);
26389        command::badline_Print(const_cast<command::src_lim&>(row), temp);
26390        str << " -badline:";
26391        strptr_PrintBash(temp,str);
26392    }
26393}
26394
26395// --- command.src_lim..NArgs
26396// Used with command lines
26397// Return # of command-line arguments that must follow this argument
26398// If FIELD is invalid, return -1
26399i32 command::src_lim_NArgs(command::FieldId field, algo::strptr& out_dflt, bool* out_anon) {
26400    i32 retval = 1;
26401    switch (field) {
26402        case command_FieldId_in: { // $comment
26403            *out_anon = false;
26404        } break;
26405        case command_FieldId_linelim: { // $comment
26406            *out_anon = false;
26407            retval=0;
26408            out_dflt="Y";
26409        } break;
26410        case command_FieldId_srcfile: { // bool: no argument required but value may be specified as linelim:Y
26411            *out_anon = false;
26412        } break;
26413        case command_FieldId_strayfile: { // bool: no argument required but value may be specified as linelim:Y
26414            *out_anon = false;
26415            retval=0;
26416            out_dflt="Y";
26417        } break;
26418        case command_FieldId_capture: { // bool: no argument required but value may be specified as strayfile:Y
26419            *out_anon = false;
26420            retval=0;
26421            out_dflt="Y";
26422        } break;
26423        case command_FieldId_write: { // bool: no argument required but value may be specified as capture:Y
26424            *out_anon = false;
26425            retval=0;
26426            out_dflt="Y";
26427        } break;
26428        case command_FieldId_badchar: { // bool: no argument required but value may be specified as write:Y
26429            *out_anon = false;
26430            retval=0;
26431            out_dflt="Y";
26432        } break;
26433        case command_FieldId_badline: { // bool: no argument required but value may be specified as badchar:Y
26434            *out_anon = false;
26435        } break;
26436        default:
26437        retval=-1; // unrecognized
26438    }
26439    return retval;
26440}
26441
26442// --- command.src_lim_proc.src_lim.Start
26443// Start subprocess
26444// If subprocess already running, do nothing. Otherwise, start it
26445int command::src_lim_Start(command::src_lim_proc& parent) {
26446    int retval = 0;
26447    if (parent.pid == 0) {
26448        verblog(src_lim_ToCmdline(parent)); // maybe print command
26449#ifdef WIN32
26450        algo_lib::ResolveExecFname(parent.path);
26451        tempstr cmdline(src_lim_ToCmdline(parent));
26452        parent.pid = dospawn(Zeroterm(parent.path),Zeroterm(cmdline),parent.timeout,parent.fstdin,parent.fstdout,parent.fstderr);
26453#else
26454        parent.pid = fork();
26455        if (parent.pid == 0) { // child
26456            algo_lib::DieWithParent();
26457            if (parent.timeout > 0) {
26458                alarm(parent.timeout);
26459            }
26460            if (retval==0) retval=algo_lib::ApplyRedirect(parent.fstdin , 0);
26461            if (retval==0) retval=algo_lib::ApplyRedirect(parent.fstdout, 1);
26462            if (retval==0) retval=algo_lib::ApplyRedirect(parent.fstderr, 2);
26463            if (retval==0) retval= src_lim_Execv(parent);
26464            if (retval != 0) { // if start fails, print error
26465                int err=errno;
26466                prerr("command.src_lim_execv"
26467                <<Keyval("errno",err)
26468                <<Keyval("errstr",strerror(err))
26469                <<Keyval("comment","Execv failed"));
26470            }
26471            _exit(127); // if failed to start, exit anyway
26472        } else if (parent.pid == -1) {
26473            retval = errno; // failed to fork
26474        }
26475#endif
26476    }
26477    parent.status = parent.pid > 0 ? 0 : -1; // if didn't start, set error status
26478    return retval;
26479}
26480
26481// --- command.src_lim_proc.src_lim.StartRead
26482// Start subprocess & Read output
26483algo::Fildes command::src_lim_StartRead(command::src_lim_proc& parent, algo_lib::FFildes &read) {
26484    int pipefd[2];
26485    int rc=pipe(pipefd);
26486    (void)rc;
26487    read.fd.value = pipefd[0];
26488    parent.fstdout  << ">&" << pipefd[1];
26489    src_lim_Start(parent);
26490    (void)close(pipefd[1]);
26491    return read.fd;
26492}
26493
26494// --- command.src_lim_proc.src_lim.Kill
26495// Kill subprocess and wait
26496void command::src_lim_Kill(command::src_lim_proc& parent) {
26497    if (parent.pid != 0) {
26498        kill(parent.pid,9);
26499        src_lim_Wait(parent);
26500    }
26501}
26502
26503// --- command.src_lim_proc.src_lim.Wait
26504// Wait for subprocess to return
26505void command::src_lim_Wait(command::src_lim_proc& parent) {
26506    if (parent.pid > 0) {
26507        int wait_flags = 0;
26508        int wait_status = 0;
26509        int rc = -1;
26510        do {
26511            // really wait for subprocess to exit
26512            rc = waitpid(parent.pid,&wait_status,wait_flags);
26513        } while (rc==-1 && errno==EINTR);
26514        if (rc == parent.pid) {
26515            parent.status = wait_status;
26516            parent.pid = 0;
26517        }
26518    }
26519}
26520
26521// --- command.src_lim_proc.src_lim.Exec
26522// Start + Wait
26523// Execute subprocess and return exit code
26524int command::src_lim_Exec(command::src_lim_proc& parent) {
26525    src_lim_Start(parent);
26526    src_lim_Wait(parent);
26527    return parent.status;
26528}
26529
26530// --- command.src_lim_proc.src_lim.ExecX
26531// Start + Wait, throw exception on error
26532// Execute subprocess; throw human-readable exception on error
26533void command::src_lim_ExecX(command::src_lim_proc& parent) {
26534    int rc = src_lim_Exec(parent);
26535    vrfy(rc==0, tempstr() << "algo_lib.exec" << Keyval("cmd",src_lim_ToCmdline(parent))
26536    << Keyval("comment",algo::DescribeWaitStatus(parent.status)));
26537}
26538
26539// --- command.src_lim_proc.src_lim.Execv
26540// Call execv()
26541// Call execv with specified parameters
26542int command::src_lim_Execv(command::src_lim_proc& parent) {
26543    int ret = 0;
26544    algo::StringAry args;
26545    src_lim_ToArgv(parent, args);
26546    char **argv = (char**)alloca((ary_N(args)+1)*sizeof(*argv));
26547    ind_beg(algo::StringAry_ary_curs,arg,args) {
26548        argv[ind_curs(arg).index] = Zeroterm(arg);
26549    }ind_end;
26550    argv[ary_N(args)] = NULL;
26551    // if parent.path is relative, search for it in PATH
26552    algo_lib::ResolveExecFname(parent.path);
26553    ret = execv(Zeroterm(parent.path),argv);
26554    return ret;
26555}
26556
26557// --- command.src_lim_proc.src_lim.ToCmdline
26558algo::tempstr command::src_lim_ToCmdline(command::src_lim_proc& parent) {
26559    algo::tempstr retval;
26560    retval << parent.path << " ";
26561    command::src_lim_PrintArgv(parent.cmd,retval);
26562    if (ch_N(parent.fstdin)) {
26563        retval << " " << parent.fstdin;
26564    }
26565    if (ch_N(parent.fstdout)) {
26566        retval << " " << parent.fstdout;
26567    }
26568    if (ch_N(parent.fstderr)) {
26569        retval << " 2" << parent.fstderr;
26570    }
26571    return retval;
26572}
26573
26574// --- command.src_lim_proc.src_lim.ToArgv
26575// Form array from the command line
26576void command::src_lim_ToArgv(command::src_lim_proc& parent, algo::StringAry& args) {
26577    ary_RemoveAll(args);
26578    ary_Alloc(args) << parent.path;
26579
26580    if (parent.cmd.in != "data") {
26581        cstring *arg = &ary_Alloc(args);
26582        *arg << "-in:";
26583        cstring_Print(parent.cmd.in, *arg);
26584    }
26585
26586    if (parent.cmd.linelim != false) {
26587        cstring *arg = &ary_Alloc(args);
26588        *arg << "-linelim:";
26589        bool_Print(parent.cmd.linelim, *arg);
26590    }
26591
26592    if (parent.cmd.srcfile.expr != "%") {
26593        cstring *arg = &ary_Alloc(args);
26594        *arg << "-srcfile:";
26595        command::srcfile_Print(parent.cmd, *arg);
26596    }
26597
26598    if (parent.cmd.strayfile != false) {
26599        cstring *arg = &ary_Alloc(args);
26600        *arg << "-strayfile:";
26601        bool_Print(parent.cmd.strayfile, *arg);
26602    }
26603
26604    if (parent.cmd.capture != false) {
26605        cstring *arg = &ary_Alloc(args);
26606        *arg << "-capture:";
26607        bool_Print(parent.cmd.capture, *arg);
26608    }
26609
26610    if (parent.cmd.write != false) {
26611        cstring *arg = &ary_Alloc(args);
26612        *arg << "-write:";
26613        bool_Print(parent.cmd.write, *arg);
26614    }
26615
26616    if (parent.cmd.badchar != false) {
26617        cstring *arg = &ary_Alloc(args);
26618        *arg << "-badchar:";
26619        bool_Print(parent.cmd.badchar, *arg);
26620    }
26621
26622    if (parent.cmd.badline.expr != "") {
26623        cstring *arg = &ary_Alloc(args);
26624        *arg << "-badline:";
26625        command::badline_Print(parent.cmd, *arg);
26626    }
26627    for (int i=1; i < algo_lib::_db.cmdline.verbose; ++i) {
26628        ary_Alloc(args) << "-verbose";
26629    }
26630}
26631
26632// --- command.src_lim_proc..Uninit
26633void command::src_lim_proc_Uninit(command::src_lim_proc& parent) {
26634    command::src_lim_proc &row = parent; (void)row;
26635
26636    // command.src_lim_proc.src_lim.Uninit (Exec)  //
26637    src_lim_Kill(parent); // kill child, ensure forward progress
26638}
26639
26640// --- command.ssim2csv..ReadFieldMaybe
26641bool command::ssim2csv_ReadFieldMaybe(command::ssim2csv& parent, algo::strptr field, algo::strptr strval) {
26642    bool retval = true;
26643    command::FieldId field_id;
26644    (void)value_SetStrptrMaybe(field_id,field);
26645    switch(field_id) {
26646        case command_FieldId_expand: {
26647            retval = algo::cstring_ReadStrptrMaybe(parent.expand, strval);
26648            break;
26649        }
26650        case command_FieldId_ignoreQuote: {
26651            retval = bool_ReadStrptrMaybe(parent.ignoreQuote, strval);
26652            break;
26653        }
26654        default: break;
26655    }
26656    if (!retval) {
26657        algo_lib::AppendErrtext("attr",field);
26658    }
26659    return retval;
26660}
26661
26662// --- command.ssim2csv..ReadTupleMaybe
26663// Read fields of command::ssim2csv from attributes of ascii tuple TUPLE
26664bool command::ssim2csv_ReadTupleMaybe(command::ssim2csv &parent, algo::Tuple &tuple) {
26665    bool retval = true;
26666    ind_beg(algo::Tuple_attrs_curs,attr,tuple) {
26667        retval = ssim2csv_ReadFieldMaybe(parent, attr.name, attr.value);
26668        if (!retval) {
26669            break;
26670        }
26671    }ind_end;
26672    return retval;
26673}
26674
26675// --- command.ssim2csv..ToCmdline
26676// Convenience function that returns a full command line
26677// Assume command is in a directory called bin
26678tempstr command::ssim2csv_ToCmdline(command::ssim2csv& row) {
26679    tempstr ret;
26680    ret << "bin/ssim2csv ";
26681    ssim2csv_PrintArgv(row, ret);
26682    // inherit less intense verbose, debug options
26683    for (int i = 1; i < algo_lib::_db.cmdline.verbose; i++) {
26684        ret << " -verbose";
26685    }
26686    for (int i = 1; i < algo_lib::_db.cmdline.debug; i++) {
26687        ret << " -debug";
26688    }
26689    return ret;
26690}
26691
26692// --- command.ssim2csv..PrintArgv
26693// print string representation of ROW to string STR
26694// cfmt:command.ssim2csv.Argv  printfmt:Auto
26695void command::ssim2csv_PrintArgv(command::ssim2csv& row, algo::cstring& str) {
26696    algo::tempstr temp;
26697    (void)temp;
26698    (void)str;
26699    if (!(row.expand == "")) {
26700        ch_RemoveAll(temp);
26701        cstring_Print(row.expand, temp);
26702        str << " -expand:";
26703        strptr_PrintBash(temp,str);
26704    }
26705    if (!(row.ignoreQuote == false)) {
26706        ch_RemoveAll(temp);
26707        bool_Print(row.ignoreQuote, temp);
26708        str << " -ignoreQuote:";
26709        strptr_PrintBash(temp,str);
26710    }
26711}
26712
26713// --- command.ssim2csv..NArgs
26714// Used with command lines
26715// Return # of command-line arguments that must follow this argument
26716// If FIELD is invalid, return -1
26717i32 command::ssim2csv_NArgs(command::FieldId field, algo::strptr& out_dflt, bool* out_anon) {
26718    i32 retval = 1;
26719    switch (field) {
26720        case command_FieldId_expand: { // $comment
26721            *out_anon = false;
26722        } break;
26723        case command_FieldId_ignoreQuote: { // $comment
26724            *out_anon = false;
26725            retval=0;
26726            out_dflt="Y";
26727        } break;
26728        default:
26729        retval=-1; // unrecognized
26730    }
26731    return retval;
26732}
26733
26734// --- command.ssim2csv_proc.ssim2csv.Start
26735// Start subprocess
26736// If subprocess already running, do nothing. Otherwise, start it
26737int command::ssim2csv_Start(command::ssim2csv_proc& parent) {
26738    int retval = 0;
26739    if (parent.pid == 0) {
26740        verblog(ssim2csv_ToCmdline(parent)); // maybe print command
26741#ifdef WIN32
26742        algo_lib::ResolveExecFname(parent.path);
26743        tempstr cmdline(ssim2csv_ToCmdline(parent));
26744        parent.pid = dospawn(Zeroterm(parent.path),Zeroterm(cmdline),parent.timeout,parent.fstdin,parent.fstdout,parent.fstderr);
26745#else
26746        parent.pid = fork();
26747        if (parent.pid == 0) { // child
26748            algo_lib::DieWithParent();
26749            if (parent.timeout > 0) {
26750                alarm(parent.timeout);
26751            }
26752            if (retval==0) retval=algo_lib::ApplyRedirect(parent.fstdin , 0);
26753            if (retval==0) retval=algo_lib::ApplyRedirect(parent.fstdout, 1);
26754            if (retval==0) retval=algo_lib::ApplyRedirect(parent.fstderr, 2);
26755            if (retval==0) retval= ssim2csv_Execv(parent);
26756            if (retval != 0) { // if start fails, print error
26757                int err=errno;
26758                prerr("command.ssim2csv_execv"
26759                <<Keyval("errno",err)
26760                <<Keyval("errstr",strerror(err))
26761                <<Keyval("comment","Execv failed"));
26762            }
26763            _exit(127); // if failed to start, exit anyway
26764        } else if (parent.pid == -1) {
26765            retval = errno; // failed to fork
26766        }
26767#endif
26768    }
26769    parent.status = parent.pid > 0 ? 0 : -1; // if didn't start, set error status
26770    return retval;
26771}
26772
26773// --- command.ssim2csv_proc.ssim2csv.StartRead
26774// Start subprocess & Read output
26775algo::Fildes command::ssim2csv_StartRead(command::ssim2csv_proc& parent, algo_lib::FFildes &read) {
26776    int pipefd[2];
26777    int rc=pipe(pipefd);
26778    (void)rc;
26779    read.fd.value = pipefd[0];
26780    parent.fstdout  << ">&" << pipefd[1];
26781    ssim2csv_Start(parent);
26782    (void)close(pipefd[1]);
26783    return read.fd;
26784}
26785
26786// --- command.ssim2csv_proc.ssim2csv.Kill
26787// Kill subprocess and wait
26788void command::ssim2csv_Kill(command::ssim2csv_proc& parent) {
26789    if (parent.pid != 0) {
26790        kill(parent.pid,9);
26791        ssim2csv_Wait(parent);
26792    }
26793}
26794
26795// --- command.ssim2csv_proc.ssim2csv.Wait
26796// Wait for subprocess to return
26797void command::ssim2csv_Wait(command::ssim2csv_proc& parent) {
26798    if (parent.pid > 0) {
26799        int wait_flags = 0;
26800        int wait_status = 0;
26801        int rc = -1;
26802        do {
26803            // really wait for subprocess to exit
26804            rc = waitpid(parent.pid,&wait_status,wait_flags);
26805        } while (rc==-1 && errno==EINTR);
26806        if (rc == parent.pid) {
26807            parent.status = wait_status;
26808            parent.pid = 0;
26809        }
26810    }
26811}
26812
26813// --- command.ssim2csv_proc.ssim2csv.Exec
26814// Start + Wait
26815// Execute subprocess and return exit code
26816int command::ssim2csv_Exec(command::ssim2csv_proc& parent) {
26817    ssim2csv_Start(parent);
26818    ssim2csv_Wait(parent);
26819    return parent.status;
26820}
26821
26822// --- command.ssim2csv_proc.ssim2csv.ExecX
26823// Start + Wait, throw exception on error
26824// Execute subprocess; throw human-readable exception on error
26825void command::ssim2csv_ExecX(command::ssim2csv_proc& parent) {
26826    int rc = ssim2csv_Exec(parent);
26827    vrfy(rc==0, tempstr() << "algo_lib.exec" << Keyval("cmd",ssim2csv_ToCmdline(parent))
26828    << Keyval("comment",algo::DescribeWaitStatus(parent.status)));
26829}
26830
26831// --- command.ssim2csv_proc.ssim2csv.Execv
26832// Call execv()
26833// Call execv with specified parameters
26834int command::ssim2csv_Execv(command::ssim2csv_proc& parent) {
26835    int ret = 0;
26836    algo::StringAry args;
26837    ssim2csv_ToArgv(parent, args);
26838    char **argv = (char**)alloca((ary_N(args)+1)*sizeof(*argv));
26839    ind_beg(algo::StringAry_ary_curs,arg,args) {
26840        argv[ind_curs(arg).index] = Zeroterm(arg);
26841    }ind_end;
26842    argv[ary_N(args)] = NULL;
26843    // if parent.path is relative, search for it in PATH
26844    algo_lib::ResolveExecFname(parent.path);
26845    ret = execv(Zeroterm(parent.path),argv);
26846    return ret;
26847}
26848
26849// --- command.ssim2csv_proc.ssim2csv.ToCmdline
26850algo::tempstr command::ssim2csv_ToCmdline(command::ssim2csv_proc& parent) {
26851    algo::tempstr retval;
26852    retval << parent.path << " ";
26853    command::ssim2csv_PrintArgv(parent.cmd,retval);
26854    if (ch_N(parent.fstdin)) {
26855        retval << " " << parent.fstdin;
26856    }
26857    if (ch_N(parent.fstdout)) {
26858        retval << " " << parent.fstdout;
26859    }
26860    if (ch_N(parent.fstderr)) {
26861        retval << " 2" << parent.fstderr;
26862    }
26863    return retval;
26864}
26865
26866// --- command.ssim2csv_proc.ssim2csv.ToArgv
26867// Form array from the command line
26868void command::ssim2csv_ToArgv(command::ssim2csv_proc& parent, algo::StringAry& args) {
26869    ary_RemoveAll(args);
26870    ary_Alloc(args) << parent.path;
26871
26872    if (parent.cmd.expand != "") {
26873        cstring *arg = &ary_Alloc(args);
26874        *arg << "-expand:";
26875        cstring_Print(parent.cmd.expand, *arg);
26876    }
26877
26878    if (parent.cmd.ignoreQuote != false) {
26879        cstring *arg = &ary_Alloc(args);
26880        *arg << "-ignoreQuote:";
26881        bool_Print(parent.cmd.ignoreQuote, *arg);
26882    }
26883    for (int i=1; i < algo_lib::_db.cmdline.verbose; ++i) {
26884        ary_Alloc(args) << "-verbose";
26885    }
26886}
26887
26888// --- command.ssim2csv_proc..Uninit
26889void command::ssim2csv_proc_Uninit(command::ssim2csv_proc& parent) {
26890    command::ssim2csv_proc &row = parent; (void)row;
26891
26892    // command.ssim2csv_proc.ssim2csv.Uninit (Exec)  //
26893    ssim2csv_Kill(parent); // kill child, ensure forward progress
26894}
26895
26896// --- command.ssim2mysql..ReadFieldMaybe
26897bool command::ssim2mysql_ReadFieldMaybe(command::ssim2mysql& parent, algo::strptr field, algo::strptr strval) {
26898    bool retval = true;
26899    command::FieldId field_id;
26900    (void)value_SetStrptrMaybe(field_id,field);
26901    switch(field_id) {
26902        case command_FieldId_url: {
26903            retval = algo::cstring_ReadStrptrMaybe(parent.url, strval);
26904            break;
26905        }
26906        case command_FieldId_data_dir: {
26907            retval = algo::cstring_ReadStrptrMaybe(parent.data_dir, strval);
26908            break;
26909        }
26910        case command_FieldId_maxpacket: {
26911            retval = i32_ReadStrptrMaybe(parent.maxpacket, strval);
26912            break;
26913        }
26914        case command_FieldId_replace: {
26915            retval = bool_ReadStrptrMaybe(parent.replace, strval);
26916            break;
26917        }
26918        case command_FieldId_trunc: {
26919            retval = bool_ReadStrptrMaybe(parent.trunc, strval);
26920            break;
26921        }
26922        case command_FieldId_dry_run: {
26923            retval = bool_ReadStrptrMaybe(parent.dry_run, strval);
26924            break;
26925        }
26926        case command_FieldId_fldfunc: {
26927            retval = bool_ReadStrptrMaybe(parent.fldfunc, strval);
26928            break;
26929        }
26930        case command_FieldId_in: {
26931            retval = algo::cstring_ReadStrptrMaybe(parent.in, strval);
26932            break;
26933        }
26934        case command_FieldId_db: {
26935            retval = algo::cstring_ReadStrptrMaybe(parent.db, strval);
26936            break;
26937        }
26938        case command_FieldId_createdb: {
26939            retval = bool_ReadStrptrMaybe(parent.createdb, strval);
26940            break;
26941        }
26942        case command_FieldId_fkey: {
26943            retval = bool_ReadStrptrMaybe(parent.fkey, strval);
26944            break;
26945        }
26946        default: break;
26947    }
26948    if (!retval) {
26949        algo_lib::AppendErrtext("attr",field);
26950    }
26951    return retval;
26952}
26953
26954// --- command.ssim2mysql..ReadTupleMaybe
26955// Read fields of command::ssim2mysql from attributes of ascii tuple TUPLE
26956bool command::ssim2mysql_ReadTupleMaybe(command::ssim2mysql &parent, algo::Tuple &tuple) {
26957    bool retval = true;
26958    ind_beg(algo::Tuple_attrs_curs,attr,tuple) {
26959        retval = ssim2mysql_ReadFieldMaybe(parent, attr.name, attr.value);
26960        if (!retval) {
26961            break;
26962        }
26963    }ind_end;
26964    return retval;
26965}
26966
26967// --- command.ssim2mysql..Init
26968// Set all fields to initial values.
26969void command::ssim2mysql_Init(command::ssim2mysql& parent) {
26970    parent.url = algo::strptr("");
26971    parent.data_dir = algo::strptr("data");
26972    parent.maxpacket = i32(100000);
26973    parent.replace = bool(true);
26974    parent.trunc = bool(false);
26975    parent.dry_run = bool(false);
26976    parent.fldfunc = bool(false);
26977    parent.in = algo::strptr("-");
26978    parent.db = algo::strptr("");
26979    parent.createdb = bool(false);
26980    parent.fkey = bool(false);
26981}
26982
26983// --- command.ssim2mysql..ToCmdline
26984// Convenience function that returns a full command line
26985// Assume command is in a directory called bin
26986tempstr command::ssim2mysql_ToCmdline(command::ssim2mysql& row) {
26987    tempstr ret;
26988    ret << "bin/ssim2mysql ";
26989    ssim2mysql_PrintArgv(row, ret);
26990    // inherit less intense verbose, debug options
26991    for (int i = 1; i < algo_lib::_db.cmdline.verbose; i++) {
26992        ret << " -verbose";
26993    }
26994    for (int i = 1; i < algo_lib::_db.cmdline.debug; i++) {
26995        ret << " -debug";
26996    }
26997    return ret;
26998}
26999
27000// --- command.ssim2mysql..PrintArgv
27001// print string representation of ROW to string STR
27002// cfmt:command.ssim2mysql.Argv  printfmt:Auto
27003void command::ssim2mysql_PrintArgv(command::ssim2mysql& row, algo::cstring& str) {
27004    algo::tempstr temp;
27005    (void)temp;
27006    (void)str;
27007    if (!(row.url == "")) {
27008        ch_RemoveAll(temp);
27009        cstring_Print(row.url, temp);
27010        str << " -url:";
27011        strptr_PrintBash(temp,str);
27012    }
27013    if (!(row.data_dir == "data")) {
27014        ch_RemoveAll(temp);
27015        cstring_Print(row.data_dir, temp);
27016        str << " -data_dir:";
27017        strptr_PrintBash(temp,str);
27018    }
27019    if (!(row.maxpacket == 100000)) {
27020        ch_RemoveAll(temp);
27021        i32_Print(row.maxpacket, temp);
27022        str << " -maxpacket:";
27023        strptr_PrintBash(temp,str);
27024    }
27025    if (!(row.replace == true)) {
27026        ch_RemoveAll(temp);
27027        bool_Print(row.replace, temp);
27028        str << " -replace:";
27029        strptr_PrintBash(temp,str);
27030    }
27031    if (!(row.trunc == false)) {
27032        ch_RemoveAll(temp);
27033        bool_Print(row.trunc, temp);
27034        str << " -trunc:";
27035        strptr_PrintBash(temp,str);
27036    }
27037    if (!(row.dry_run == false)) {
27038        ch_RemoveAll(temp);
27039        bool_Print(row.dry_run, temp);
27040        str << " -dry_run:";
27041        strptr_PrintBash(temp,str);
27042    }
27043    if (!(row.fldfunc == false)) {
27044        ch_RemoveAll(temp);
27045        bool_Print(row.fldfunc, temp);
27046        str << " -fldfunc:";
27047        strptr_PrintBash(temp,str);
27048    }
27049    if (!(row.in == "-")) {
27050        ch_RemoveAll(temp);
27051        cstring_Print(row.in, temp);
27052        str << " -in:";
27053        strptr_PrintBash(temp,str);
27054    }
27055    if (!(row.db == "")) {
27056        ch_RemoveAll(temp);
27057        cstring_Print(row.db, temp);
27058        str << " -db:";
27059        strptr_PrintBash(temp,str);
27060    }
27061    if (!(row.createdb == false)) {
27062        ch_RemoveAll(temp);
27063        bool_Print(row.createdb, temp);
27064        str << " -createdb:";
27065        strptr_PrintBash(temp,str);
27066    }
27067    if (!(row.fkey == false)) {
27068        ch_RemoveAll(temp);
27069        bool_Print(row.fkey, temp);
27070        str << " -fkey:";
27071        strptr_PrintBash(temp,str);
27072    }
27073}
27074
27075// --- command.ssim2mysql..NArgs
27076// Used with command lines
27077// Return # of command-line arguments that must follow this argument
27078// If FIELD is invalid, return -1
27079i32 command::ssim2mysql_NArgs(command::FieldId field, algo::strptr& out_dflt, bool* out_anon) {
27080    i32 retval = 1;
27081    switch (field) {
27082        case command_FieldId_url: { // $comment
27083            *out_anon = false;
27084        } break;
27085        case command_FieldId_data_dir: { // $comment
27086            *out_anon = false;
27087        } break;
27088        case command_FieldId_maxpacket: { // $comment
27089            *out_anon = false;
27090        } break;
27091        case command_FieldId_replace: { // $comment
27092            *out_anon = false;
27093            retval=0;
27094            out_dflt="Y";
27095        } break;
27096        case command_FieldId_trunc: { // bool: no argument required but value may be specified as replace:Y
27097            *out_anon = false;
27098            retval=0;
27099            out_dflt="Y";
27100        } break;
27101        case command_FieldId_dry_run: { // bool: no argument required but value may be specified as trunc:Y
27102            *out_anon = false;
27103            retval=0;
27104            out_dflt="Y";
27105        } break;
27106        case command_FieldId_fldfunc: { // bool: no argument required but value may be specified as dry_run:Y
27107            *out_anon = false;
27108            retval=0;
27109            out_dflt="Y";
27110        } break;
27111        case command_FieldId_in: { // bool: no argument required but value may be specified as fldfunc:Y
27112            *out_anon = false;
27113        } break;
27114        case command_FieldId_db: { // bool: no argument required but value may be specified as fldfunc:Y
27115            *out_anon = false;
27116        } break;
27117        case command_FieldId_createdb: { // bool: no argument required but value may be specified as fldfunc:Y
27118            *out_anon = false;
27119            retval=0;
27120            out_dflt="Y";
27121        } break;
27122        case command_FieldId_fkey: { // bool: no argument required but value may be specified as createdb:Y
27123            *out_anon = false;
27124            retval=0;
27125            out_dflt="Y";
27126        } break;
27127        default:
27128        retval=-1; // unrecognized
27129    }
27130    return retval;
27131}
27132
27133// --- command.ssim2mysql_proc.ssim2mysql.Start
27134// Start subprocess
27135// If subprocess already running, do nothing. Otherwise, start it
27136int command::ssim2mysql_Start(command::ssim2mysql_proc& parent) {
27137    int retval = 0;
27138    if (parent.pid == 0) {
27139        verblog(ssim2mysql_ToCmdline(parent)); // maybe print command
27140#ifdef WIN32
27141        algo_lib::ResolveExecFname(parent.path);
27142        tempstr cmdline(ssim2mysql_ToCmdline(parent));
27143        parent.pid = dospawn(Zeroterm(parent.path),Zeroterm(cmdline),parent.timeout,parent.fstdin,parent.fstdout,parent.fstderr);
27144#else
27145        parent.pid = fork();
27146        if (parent.pid == 0) { // child
27147            algo_lib::DieWithParent();
27148            if (parent.timeout > 0) {
27149                alarm(parent.timeout);
27150            }
27151            if (retval==0) retval=algo_lib::ApplyRedirect(parent.fstdin , 0);
27152            if (retval==0) retval=algo_lib::ApplyRedirect(parent.fstdout, 1);
27153            if (retval==0) retval=algo_lib::ApplyRedirect(parent.fstderr, 2);
27154            if (retval==0) retval= ssim2mysql_Execv(parent);
27155            if (retval != 0) { // if start fails, print error
27156                int err=errno;
27157                prerr("command.ssim2mysql_execv"
27158                <<Keyval("errno",err)
27159                <<Keyval("errstr",strerror(err))
27160                <<Keyval("comment","Execv failed"));
27161            }
27162            _exit(127); // if failed to start, exit anyway
27163        } else if (parent.pid == -1) {
27164            retval = errno; // failed to fork
27165        }
27166#endif
27167    }
27168    parent.status = parent.pid > 0 ? 0 : -1; // if didn't start, set error status
27169    return retval;
27170}
27171
27172// --- command.ssim2mysql_proc.ssim2mysql.StartRead
27173// Start subprocess & Read output
27174algo::Fildes command::ssim2mysql_StartRead(command::ssim2mysql_proc& parent, algo_lib::FFildes &read) {
27175    int pipefd[2];
27176    int rc=pipe(pipefd);
27177    (void)rc;
27178    read.fd.value = pipefd[0];
27179    parent.fstdout  << ">&" << pipefd[1];
27180    ssim2mysql_Start(parent);
27181    (void)close(pipefd[1]);
27182    return read.fd;
27183}
27184
27185// --- command.ssim2mysql_proc.ssim2mysql.Kill
27186// Kill subprocess and wait
27187void command::ssim2mysql_Kill(command::ssim2mysql_proc& parent) {
27188    if (parent.pid != 0) {
27189        kill(parent.pid,9);
27190        ssim2mysql_Wait(parent);
27191    }
27192}
27193
27194// --- command.ssim2mysql_proc.ssim2mysql.Wait
27195// Wait for subprocess to return
27196void command::ssim2mysql_Wait(command::ssim2mysql_proc& parent) {
27197    if (parent.pid > 0) {
27198        int wait_flags = 0;
27199        int wait_status = 0;
27200        int rc = -1;
27201        do {
27202            // really wait for subprocess to exit
27203            rc = waitpid(parent.pid,&wait_status,wait_flags);
27204        } while (rc==-1 && errno==EINTR);
27205        if (rc == parent.pid) {
27206            parent.status = wait_status;
27207            parent.pid = 0;
27208        }
27209    }
27210}
27211
27212// --- command.ssim2mysql_proc.ssim2mysql.Exec
27213// Start + Wait
27214// Execute subprocess and return exit code
27215int command::ssim2mysql_Exec(command::ssim2mysql_proc& parent) {
27216    ssim2mysql_Start(parent);
27217    ssim2mysql_Wait(parent);
27218    return parent.status;
27219}
27220
27221// --- command.ssim2mysql_proc.ssim2mysql.ExecX
27222// Start + Wait, throw exception on error
27223// Execute subprocess; throw human-readable exception on error
27224void command::ssim2mysql_ExecX(command::ssim2mysql_proc& parent) {
27225    int rc = ssim2mysql_Exec(parent);
27226    vrfy(rc==0, tempstr() << "algo_lib.exec" << Keyval("cmd",ssim2mysql_ToCmdline(parent))
27227    << Keyval("comment",algo::DescribeWaitStatus(parent.status)));
27228}
27229
27230// --- command.ssim2mysql_proc.ssim2mysql.Execv
27231// Call execv()
27232// Call execv with specified parameters
27233int command::ssim2mysql_Execv(command::ssim2mysql_proc& parent) {
27234    int ret = 0;
27235    algo::StringAry args;
27236    ssim2mysql_ToArgv(parent, args);
27237    char **argv = (char**)alloca((ary_N(args)+1)*sizeof(*argv));
27238    ind_beg(algo::StringAry_ary_curs,arg,args) {
27239        argv[ind_curs(arg).index] = Zeroterm(arg);
27240    }ind_end;
27241    argv[ary_N(args)] = NULL;
27242    // if parent.path is relative, search for it in PATH
27243    algo_lib::ResolveExecFname(parent.path);
27244    ret = execv(Zeroterm(parent.path),argv);
27245    return ret;
27246}
27247
27248// --- command.ssim2mysql_proc.ssim2mysql.ToCmdline
27249algo::tempstr command::ssim2mysql_ToCmdline(command::ssim2mysql_proc& parent) {
27250    algo::tempstr retval;
27251    retval << parent.path << " ";
27252    command::ssim2mysql_PrintArgv(parent.cmd,retval);
27253    if (ch_N(parent.fstdin)) {
27254        retval << " " << parent.fstdin;
27255    }
27256    if (ch_N(parent.fstdout)) {
27257        retval << " " << parent.fstdout;
27258    }
27259    if (ch_N(parent.fstderr)) {
27260        retval << " 2" << parent.fstderr;
27261    }
27262    return retval;
27263}
27264
27265// --- command.ssim2mysql_proc.ssim2mysql.ToArgv
27266// Form array from the command line
27267void command::ssim2mysql_ToArgv(command::ssim2mysql_proc& parent, algo::StringAry& args) {
27268    ary_RemoveAll(args);
27269    ary_Alloc(args) << parent.path;
27270
27271    if (parent.cmd.url != "") {
27272        cstring *arg = &ary_Alloc(args);
27273        *arg << "-url:";
27274        cstring_Print(parent.cmd.url, *arg);
27275    }
27276
27277    if (parent.cmd.data_dir != "data") {
27278        cstring *arg = &ary_Alloc(args);
27279        *arg << "-data_dir:";
27280        cstring_Print(parent.cmd.data_dir, *arg);
27281    }
27282
27283    if (parent.cmd.maxpacket != 100000) {
27284        cstring *arg = &ary_Alloc(args);
27285        *arg << "-maxpacket:";
27286        i32_Print(parent.cmd.maxpacket, *arg);
27287    }
27288
27289    if (parent.cmd.replace != true) {
27290        cstring *arg = &ary_Alloc(args);
27291        *arg << "-replace:";
27292        bool_Print(parent.cmd.replace, *arg);
27293    }
27294
27295    if (parent.cmd.trunc != false) {
27296        cstring *arg = &ary_Alloc(args);
27297        *arg << "-trunc:";
27298        bool_Print(parent.cmd.trunc, *arg);
27299    }
27300
27301    if (parent.cmd.dry_run != false) {
27302        cstring *arg = &ary_Alloc(args);
27303        *arg << "-dry_run:";
27304        bool_Print(parent.cmd.dry_run, *arg);
27305    }
27306
27307    if (parent.cmd.fldfunc != false) {
27308        cstring *arg = &ary_Alloc(args);
27309        *arg << "-fldfunc:";
27310        bool_Print(parent.cmd.fldfunc, *arg);
27311    }
27312
27313    if (parent.cmd.in != "-") {
27314        cstring *arg = &ary_Alloc(args);
27315        *arg << "-in:";
27316        cstring_Print(parent.cmd.in, *arg);
27317    }
27318
27319    if (parent.cmd.db != "") {
27320        cstring *arg = &ary_Alloc(args);
27321        *arg << "-db:";
27322        cstring_Print(parent.cmd.db, *arg);
27323    }
27324
27325    if (parent.cmd.createdb != false) {
27326        cstring *arg = &ary_Alloc(args);
27327        *arg << "-createdb:";
27328        bool_Print(parent.cmd.createdb, *arg);
27329    }
27330
27331    if (parent.cmd.fkey != false) {
27332        cstring *arg = &ary_Alloc(args);
27333        *arg << "-fkey:";
27334        bool_Print(parent.cmd.fkey, *arg);
27335    }
27336    for (int i=1; i < algo_lib::_db.cmdline.verbose; ++i) {
27337        ary_Alloc(args) << "-verbose";
27338    }
27339}
27340
27341// --- command.ssim2mysql_proc..Uninit
27342void command::ssim2mysql_proc_Uninit(command::ssim2mysql_proc& parent) {
27343    command::ssim2mysql_proc &row = parent; (void)row;
27344
27345    // command.ssim2mysql_proc.ssim2mysql.Uninit (Exec)  //
27346    ssim2mysql_Kill(parent); // kill child, ensure forward progress
27347}
27348
27349// --- command.ssimfilt.typetag.Print
27350// Print back to string
27351void command::typetag_Print(command::ssimfilt& parent, algo::cstring &out) {
27352    Regx_Print(parent.typetag, out);
27353}
27354
27355// --- command.ssimfilt.typetag.ReadStrptrMaybe
27356// Read Regx from string
27357// Convert string to field. Return success value
27358bool command::typetag_ReadStrptrMaybe(command::ssimfilt& parent, algo::strptr in) {
27359    bool retval = true;
27360    Regx_ReadSql(parent.typetag, in, true);
27361    return retval;
27362}
27363
27364// --- command.ssimfilt.match.Addary
27365// Reserve space (this may move memory). Insert N element at the end.
27366// Return aryptr to newly inserted block.
27367// If the RHS argument aliases the array (refers to the same memory), exit program with fatal error.
27368algo::aryptr<algo::cstring> command::match_Addary(command::ssimfilt& parent, algo::aryptr<algo::cstring> rhs) {
27369    bool overlaps = rhs.n_elems>0 && rhs.elems >= parent.match_elems && rhs.elems < parent.match_elems + parent.match_max;
27370    if (UNLIKELY(overlaps)) {
27371        FatalErrorExit("command.tary_alias  field:command.ssimfilt.match  comment:'alias error: sub-array is being appended to the whole'");
27372    }
27373    int nnew = rhs.n_elems;
27374    match_Reserve(parent, nnew); // reserve space
27375    int at = parent.match_n;
27376    for (int i = 0; i < nnew; i++) {
27377        new (parent.match_elems + at + i) algo::cstring(rhs[i]);
27378        parent.match_n++;
27379    }
27380    return algo::aryptr<algo::cstring>(parent.match_elems + at, nnew);
27381}
27382
27383// --- command.ssimfilt.match.Alloc
27384// Reserve space. Insert element at the end
27385// The new element is initialized to a default value
27386algo::cstring& command::match_Alloc(command::ssimfilt& parent) {
27387    match_Reserve(parent, 1);
27388    int n  = parent.match_n;
27389    int at = n;
27390    algo::cstring *elems = parent.match_elems;
27391    new (elems + at) algo::cstring(); // construct new element, default initializer
27392    parent.match_n = n+1;
27393    return elems[at];
27394}
27395
27396// --- command.ssimfilt.match.AllocAt
27397// Reserve space for new element, reallocating the array if necessary
27398// Insert new element at specified index. Index must be in range or a fatal error occurs.
27399algo::cstring& command::match_AllocAt(command::ssimfilt& parent, int at) {
27400    match_Reserve(parent, 1);
27401    int n  = parent.match_n;
27402    if (UNLIKELY(u64(at) >= u64(n+1))) {
27403        FatalErrorExit("command.bad_alloc_at  field:command.ssimfilt.match  comment:'index out of range'");
27404    }
27405    algo::cstring *elems = parent.match_elems;
27406    memmove(elems + at + 1, elems + at, (n - at) * sizeof(algo::cstring));
27407    new (elems + at) algo::cstring(); // construct element, default initializer
27408    parent.match_n = n+1;
27409    return elems[at];
27410}
27411
27412// --- command.ssimfilt.match.AllocN
27413// Reserve space. Insert N elements at the end of the array, return pointer to array
27414algo::aryptr<algo::cstring> command::match_AllocN(command::ssimfilt& parent, int n_elems) {
27415    match_Reserve(parent, n_elems);
27416    int old_n  = parent.match_n;
27417    int new_n = old_n + n_elems;
27418    algo::cstring *elems = parent.match_elems;
27419    for (int i = old_n; i < new_n; i++) {
27420        new (elems + i) algo::cstring(); // construct new element, default initialize
27421    }
27422    parent.match_n = new_n;
27423    return algo::aryptr<algo::cstring>(elems + old_n, n_elems);
27424}
27425
27426// --- command.ssimfilt.match.Remove
27427// Remove item by index. If index outside of range, do nothing.
27428void command::match_Remove(command::ssimfilt& parent, u32 i) {
27429    u32 lim = parent.match_n;
27430    algo::cstring *elems = parent.match_elems;
27431    if (i < lim) {
27432        elems[i].~cstring(); // destroy element
27433        memmove(elems + i, elems + (i + 1), sizeof(algo::cstring) * (lim - (i + 1)));
27434        parent.match_n = lim - 1;
27435    }
27436}
27437
27438// --- command.ssimfilt.match.RemoveAll
27439void command::match_RemoveAll(command::ssimfilt& parent) {
27440    u32 n = parent.match_n;
27441    while (n > 0) {
27442        n -= 1;
27443        parent.match_elems[n].~cstring();
27444        parent.match_n = n;
27445    }
27446}
27447
27448// --- command.ssimfilt.match.RemoveLast
27449// Delete last element of array. Do nothing if array is empty.
27450void command::match_RemoveLast(command::ssimfilt& parent) {
27451    u64 n = parent.match_n;
27452    if (n > 0) {
27453        n -= 1;
27454        match_qFind(parent, u64(n)).~cstring();
27455        parent.match_n = n;
27456    }
27457}
27458
27459// --- command.ssimfilt.match.AbsReserve
27460// Make sure N elements fit in array. Process dies if out of memory
27461void command::match_AbsReserve(command::ssimfilt& parent, int n) {
27462    u32 old_max  = parent.match_max;
27463    if (n > i32(old_max)) {
27464        u32 new_max  = i32_Max(i32_Max(old_max * 2, n), 4);
27465        void *new_mem = algo_lib::malloc_ReallocMem(parent.match_elems, old_max * sizeof(algo::cstring), new_max * sizeof(algo::cstring));
27466        if (UNLIKELY(!new_mem)) {
27467            FatalErrorExit("command.tary_nomem  field:command.ssimfilt.match  comment:'out of memory'");
27468        }
27469        parent.match_elems = (algo::cstring*)new_mem;
27470        parent.match_max = new_max;
27471    }
27472}
27473
27474// --- command.ssimfilt.match.Setary
27475// Copy contents of RHS to PARENT.
27476void command::match_Setary(command::ssimfilt& parent, command::ssimfilt &rhs) {
27477    match_RemoveAll(parent);
27478    int nnew = rhs.match_n;
27479    match_Reserve(parent, nnew); // reserve space
27480    for (int i = 0; i < nnew; i++) { // copy elements over
27481        new (parent.match_elems + i) algo::cstring(match_qFind(rhs, i));
27482        parent.match_n = i + 1;
27483    }
27484}
27485
27486// --- command.ssimfilt.match.Setary2
27487// Copy specified array into match, discarding previous contents.
27488// If the RHS argument aliases the array (refers to the same memory), throw exception.
27489void command::match_Setary(command::ssimfilt& parent, const algo::aryptr<algo::cstring> &rhs) {
27490    match_RemoveAll(parent);
27491    match_Addary(parent, rhs);
27492}
27493
27494// --- command.ssimfilt.match.AllocNVal
27495// Reserve space. Insert N elements at the end of the array, return pointer to array
27496algo::aryptr<algo::cstring> command::match_AllocNVal(command::ssimfilt& parent, int n_elems, const algo::cstring& val) {
27497    match_Reserve(parent, n_elems);
27498    int old_n  = parent.match_n;
27499    int new_n = old_n + n_elems;
27500    algo::cstring *elems = parent.match_elems;
27501    for (int i = old_n; i < new_n; i++) {
27502        new (elems + i) algo::cstring(val);
27503    }
27504    parent.match_n = new_n;
27505    return algo::aryptr<algo::cstring>(elems + old_n, n_elems);
27506}
27507
27508// --- command.ssimfilt.match.ReadStrptrMaybe
27509// A single element is read from input string and appended to the array.
27510// If the string contains an error, the array is untouched.
27511// Function returns success value.
27512bool command::match_ReadStrptrMaybe(command::ssimfilt& parent, algo::strptr in_str) {
27513    bool retval = true;
27514    algo::cstring &elem = match_Alloc(parent);
27515    retval = algo::cstring_ReadStrptrMaybe(elem, in_str);
27516    if (!retval) {
27517        match_RemoveLast(parent);
27518    }
27519    return retval;
27520}
27521
27522// --- command.ssimfilt.field.Addary
27523// Reserve space (this may move memory). Insert N element at the end.
27524// Return aryptr to newly inserted block.
27525// If the RHS argument aliases the array (refers to the same memory), exit program with fatal error.
27526algo::aryptr<algo::cstring> command::field_Addary(command::ssimfilt& parent, algo::aryptr<algo::cstring> rhs) {
27527    bool overlaps = rhs.n_elems>0 && rhs.elems >= parent.field_elems && rhs.elems < parent.field_elems + parent.field_max;
27528    if (UNLIKELY(overlaps)) {
27529        FatalErrorExit("command.tary_alias  field:command.ssimfilt.field  comment:'alias error: sub-array is being appended to the whole'");
27530    }
27531    int nnew = rhs.n_elems;
27532    field_Reserve(parent, nnew); // reserve space
27533    int at = parent.field_n;
27534    for (int i = 0; i < nnew; i++) {
27535        new (parent.field_elems + at + i) algo::cstring(rhs[i]);
27536        parent.field_n++;
27537    }
27538    return algo::aryptr<algo::cstring>(parent.field_elems + at, nnew);
27539}
27540
27541// --- command.ssimfilt.field.Alloc
27542// Reserve space. Insert element at the end
27543// The new element is initialized to a default value
27544algo::cstring& command::field_Alloc(command::ssimfilt& parent) {
27545    field_Reserve(parent, 1);
27546    int n  = parent.field_n;
27547    int at = n;
27548    algo::cstring *elems = parent.field_elems;
27549    new (elems + at) algo::cstring(); // construct new element, default initializer
27550    parent.field_n = n+1;
27551    return elems[at];
27552}
27553
27554// --- command.ssimfilt.field.AllocAt
27555// Reserve space for new element, reallocating the array if necessary
27556// Insert new element at specified index. Index must be in range or a fatal error occurs.
27557algo::cstring& command::field_AllocAt(command::ssimfilt& parent, int at) {
27558    field_Reserve(parent, 1);
27559    int n  = parent.field_n;
27560    if (UNLIKELY(u64(at) >= u64(n+1))) {
27561        FatalErrorExit("command.bad_alloc_at  field:command.ssimfilt.field  comment:'index out of range'");
27562    }
27563    algo::cstring *elems = parent.field_elems;
27564    memmove(elems + at + 1, elems + at, (n - at) * sizeof(algo::cstring));
27565    new (elems + at) algo::cstring(); // construct element, default initializer
27566    parent.field_n = n+1;
27567    return elems[at];
27568}
27569
27570// --- command.ssimfilt.field.AllocN
27571// Reserve space. Insert N elements at the end of the array, return pointer to array
27572algo::aryptr<algo::cstring> command::field_AllocN(command::ssimfilt& parent, int n_elems) {
27573    field_Reserve(parent, n_elems);
27574    int old_n  = parent.field_n;
27575    int new_n = old_n + n_elems;
27576    algo::cstring *elems = parent.field_elems;
27577    for (int i = old_n; i < new_n; i++) {
27578        new (elems + i) algo::cstring(); // construct new element, default initialize
27579    }
27580    parent.field_n = new_n;
27581    return algo::aryptr<algo::cstring>(elems + old_n, n_elems);
27582}
27583
27584// --- command.ssimfilt.field.Remove
27585// Remove item by index. If index outside of range, do nothing.
27586void command::field_Remove(command::ssimfilt& parent, u32 i) {
27587    u32 lim = parent.field_n;
27588    algo::cstring *elems = parent.field_elems;
27589    if (i < lim) {
27590        elems[i].~cstring(); // destroy element
27591        memmove(elems + i, elems + (i + 1), sizeof(algo::cstring) * (lim - (i + 1)));
27592        parent.field_n = lim - 1;
27593    }
27594}
27595
27596// --- command.ssimfilt.field.RemoveAll
27597void command::field_RemoveAll(command::ssimfilt& parent) {
27598    u32 n = parent.field_n;
27599    while (n > 0) {
27600        n -= 1;
27601        parent.field_elems[n].~cstring();
27602        parent.field_n = n;
27603    }
27604}
27605
27606// --- command.ssimfilt.field.RemoveLast
27607// Delete last element of array. Do nothing if array is empty.
27608void command::field_RemoveLast(command::ssimfilt& parent) {
27609    u64 n = parent.field_n;
27610    if (n > 0) {
27611        n -= 1;
27612        field_qFind(parent, u64(n)).~cstring();
27613        parent.field_n = n;
27614    }
27615}
27616
27617// --- command.ssimfilt.field.AbsReserve
27618// Make sure N elements fit in array. Process dies if out of memory
27619void command::field_AbsReserve(command::ssimfilt& parent, int n) {
27620    u32 old_max  = parent.field_max;
27621    if (n > i32(old_max)) {
27622        u32 new_max  = i32_Max(i32_Max(old_max * 2, n), 4);
27623        void *new_mem = algo_lib::malloc_ReallocMem(parent.field_elems, old_max * sizeof(algo::cstring), new_max * sizeof(algo::cstring));
27624        if (UNLIKELY(!new_mem)) {
27625            FatalErrorExit("command.tary_nomem  field:command.ssimfilt.field  comment:'out of memory'");
27626        }
27627        parent.field_elems = (algo::cstring*)new_mem;
27628        parent.field_max = new_max;
27629    }
27630}
27631
27632// --- command.ssimfilt.field.Setary
27633// Copy contents of RHS to PARENT.
27634void command::field_Setary(command::ssimfilt& parent, command::ssimfilt &rhs) {
27635    field_RemoveAll(parent);
27636    int nnew = rhs.field_n;
27637    field_Reserve(parent, nnew); // reserve space
27638    for (int i = 0; i < nnew; i++) { // copy elements over
27639        new (parent.field_elems + i) algo::cstring(field_qFind(rhs, i));
27640        parent.field_n = i + 1;
27641    }
27642}
27643
27644// --- command.ssimfilt.field.Setary2
27645// Copy specified array into field, discarding previous contents.
27646// If the RHS argument aliases the array (refers to the same memory), throw exception.
27647void command::field_Setary(command::ssimfilt& parent, const algo::aryptr<algo::cstring> &rhs) {
27648    field_RemoveAll(parent);
27649    field_Addary(parent, rhs);
27650}
27651
27652// --- command.ssimfilt.field.AllocNVal
27653// Reserve space. Insert N elements at the end of the array, return pointer to array
27654algo::aryptr<algo::cstring> command::field_AllocNVal(command::ssimfilt& parent, int n_elems, const algo::cstring& val) {
27655    field_Reserve(parent, n_elems);
27656    int old_n  = parent.field_n;
27657    int new_n = old_n + n_elems;
27658    algo::cstring *elems = parent.field_elems;
27659    for (int i = old_n; i < new_n; i++) {
27660        new (elems + i) algo::cstring(val);
27661    }
27662    parent.field_n = new_n;
27663    return algo::aryptr<algo::cstring>(elems + old_n, n_elems);
27664}
27665
27666// --- command.ssimfilt.field.ReadStrptrMaybe
27667// A single element is read from input string and appended to the array.
27668// If the string contains an error, the array is untouched.
27669// Function returns success value.
27670bool command::field_ReadStrptrMaybe(command::ssimfilt& parent, algo::strptr in_str) {
27671    bool retval = true;
27672    algo::cstring &elem = field_Alloc(parent);
27673    retval = algo::cstring_ReadStrptrMaybe(elem, in_str);
27674    if (!retval) {
27675        field_RemoveLast(parent);
27676    }
27677    return retval;
27678}
27679
27680// --- command.ssimfilt.format.ToCstr
27681// Convert numeric value of field to one of predefined string constants.
27682// If string is found, return a static C string. Otherwise, return NULL.
27683const char* command::format_ToCstr(const command::ssimfilt& parent) {
27684    const char *ret = NULL;
27685    switch(format_GetEnum(parent)) {
27686        case command_ssimfilt_format_ssim  : ret = "ssim";  break;
27687        case command_ssimfilt_format_csv   : ret = "csv";  break;
27688        case command_ssimfilt_format_field : ret = "field";  break;
27689        case command_ssimfilt_format_cmd   : ret = "cmd";  break;
27690        case command_ssimfilt_format_json  : ret = "json";  break;
27691        case command_ssimfilt_format_table : ret = "table";  break;
27692    }
27693    return ret;
27694}
27695
27696// --- command.ssimfilt.format.Print
27697// Convert format to a string. First, attempt conversion to a known string.
27698// If no string matches, print format as a numeric value.
27699void command::format_Print(const command::ssimfilt& parent, algo::cstring &lhs) {
27700    const char *strval = format_ToCstr(parent);
27701    if (strval) {
27702        lhs << strval;
27703    } else {
27704        lhs << parent.format;
27705    }
27706}
27707
27708// --- command.ssimfilt.format.SetStrptrMaybe
27709// Convert string to field.
27710// If the string is invalid, do not modify field and return false.
27711// In case of success, return true
27712bool command::format_SetStrptrMaybe(command::ssimfilt& parent, algo::strptr rhs) {
27713    bool ret = false;
27714    switch (elems_N(rhs)) {
27715        case 3: {
27716            switch (u64(algo::ReadLE16(rhs.elems))|(u64(rhs[2])<<16)) {
27717                case LE_STR3('c','m','d'): {
27718                    format_SetEnum(parent,command_ssimfilt_format_cmd); ret = true; break;
27719                }
27720                case LE_STR3('c','s','v'): {
27721                    format_SetEnum(parent,command_ssimfilt_format_csv); ret = true; break;
27722                }
27723            }
27724            break;
27725        }
27726        case 4: {
27727            switch (u64(algo::ReadLE32(rhs.elems))) {
27728                case LE_STR4('j','s','o','n'): {
27729                    format_SetEnum(parent,command_ssimfilt_format_json); ret = true; break;
27730                }
27731                case LE_STR4('s','s','i','m'): {
27732                    format_SetEnum(parent,command_ssimfilt_format_ssim); ret = true; break;
27733                }
27734            }
27735            break;
27736        }
27737        case 5: {
27738            switch (u64(algo::ReadLE32(rhs.elems))|(u64(rhs[4])<<32)) {
27739                case LE_STR5('f','i','e','l','d'): {
27740                    format_SetEnum(parent,command_ssimfilt_format_field); ret = true; break;
27741                }
27742                case LE_STR5('t','a','b','l','e'): {
27743                    format_SetEnum(parent,command_ssimfilt_format_table); ret = true; break;
27744                }
27745            }
27746            break;
27747        }
27748    }
27749    return ret;
27750}
27751
27752// --- command.ssimfilt.format.SetStrptr
27753// Convert string to field.
27754// If the string is invalid, set numeric value to DFLT
27755void command::format_SetStrptr(command::ssimfilt& parent, algo::strptr rhs, command_ssimfilt_format_Enum dflt) {
27756    if (!format_SetStrptrMaybe(parent,rhs)) format_SetEnum(parent,dflt);
27757}
27758
27759// --- command.ssimfilt.format.ReadStrptrMaybe
27760// Convert string to field. Return success value
27761bool command::format_ReadStrptrMaybe(command::ssimfilt& parent, algo::strptr rhs) {
27762    bool retval = false;
27763    retval = format_SetStrptrMaybe(parent,rhs); // try symbol conversion
27764    if (!retval) { // didn't work? try reading as underlying type
27765        retval = u8_ReadStrptrMaybe(parent.format,rhs);
27766    }
27767    return retval;
27768}
27769
27770// --- command.ssimfilt..ReadFieldMaybe
27771bool command::ssimfilt_ReadFieldMaybe(command::ssimfilt& parent, algo::strptr field, algo::strptr strval) {
27772    bool retval = true;
27773    command::FieldId field_id;
27774    (void)value_SetStrptrMaybe(field_id,algo::Pathcomp(field, ".LL"));
27775    switch(field_id) {
27776        case command_FieldId_in: {
27777            retval = algo::cstring_ReadStrptrMaybe(parent.in, strval);
27778            break;
27779        }
27780        case command_FieldId_typetag: {
27781            retval = typetag_ReadStrptrMaybe(parent, strval);
27782            break;
27783        }
27784        case command_FieldId_match: {
27785            retval = match_ReadStrptrMaybe(parent, strval);
27786            break;
27787        }
27788        case command_FieldId_field: {
27789            retval = field_ReadStrptrMaybe(parent, strval);
27790            break;
27791        }
27792        case command_FieldId_format: {
27793            retval = format_ReadStrptrMaybe(parent, strval);
27794            break;
27795        }
27796        case command_FieldId_t: {
27797            retval = bool_ReadStrptrMaybe(parent.t, strval);
27798            break;
27799        }
27800        case command_FieldId_cmd: {
27801            retval = algo::cstring_ReadStrptrMaybe(parent.cmd, strval);
27802            break;
27803        }
27804        default: break;
27805    }
27806    if (!retval) {
27807        algo_lib::AppendErrtext("attr",field);
27808    }
27809    return retval;
27810}
27811
27812// --- command.ssimfilt..ReadTupleMaybe
27813// Read fields of command::ssimfilt from attributes of ascii tuple TUPLE
27814bool command::ssimfilt_ReadTupleMaybe(command::ssimfilt &parent, algo::Tuple &tuple) {
27815    bool retval = true;
27816    int anon_idx = 0;
27817    ind_beg(algo::Tuple_attrs_curs,attr,tuple) {
27818        if (ch_N(attr.name) == 0) {
27819            attr.name = ssimfilt_GetAnon(parent, anon_idx++);
27820        }
27821        retval = ssimfilt_ReadFieldMaybe(parent, attr.name, attr.value);
27822        if (!retval) {
27823            break;
27824        }
27825    }ind_end;
27826    return retval;
27827}
27828
27829// --- command.ssimfilt..Init
27830// Set all fields to initial values.
27831void command::ssimfilt_Init(command::ssimfilt& parent) {
27832    parent.in = algo::strptr("data");
27833    Regx_ReadSql(parent.typetag, "%", true);
27834    parent.match_elems 	= 0; // (command.ssimfilt.match)
27835    parent.match_n     	= 0; // (command.ssimfilt.match)
27836    parent.match_max   	= 0; // (command.ssimfilt.match)
27837    parent.field_elems 	= 0; // (command.ssimfilt.field)
27838    parent.field_n     	= 0; // (command.ssimfilt.field)
27839    parent.field_max   	= 0; // (command.ssimfilt.field)
27840    parent.format = u8(0);
27841    parent.t = bool(false);
27842    parent.cmd = algo::strptr("");
27843}
27844
27845// --- command.ssimfilt..Uninit
27846void command::ssimfilt_Uninit(command::ssimfilt& parent) {
27847    command::ssimfilt &row = parent; (void)row;
27848
27849    // command.ssimfilt.field.Uninit (Tary)  //(project) Select fields for output (regx)
27850    // remove all elements from command.ssimfilt.field
27851    field_RemoveAll(parent);
27852    // free memory for Tary command.ssimfilt.field
27853    algo_lib::malloc_FreeMem(parent.field_elems, sizeof(algo::cstring)*parent.field_max); // (command.ssimfilt.field)
27854
27855    // command.ssimfilt.match.Uninit (Tary)  //(filter) Select input tuple if value of key matches value (regx:regx)
27856    // remove all elements from command.ssimfilt.match
27857    match_RemoveAll(parent);
27858    // free memory for Tary command.ssimfilt.match
27859    algo_lib::malloc_FreeMem(parent.match_elems, sizeof(algo::cstring)*parent.match_max); // (command.ssimfilt.match)
27860}
27861
27862// --- command.ssimfilt..ToCmdline
27863// Convenience function that returns a full command line
27864// Assume command is in a directory called bin
27865tempstr command::ssimfilt_ToCmdline(command::ssimfilt& row) {
27866    tempstr ret;
27867    ret << "bin/ssimfilt ";
27868    ssimfilt_PrintArgv(row, ret);
27869    // inherit less intense verbose, debug options
27870    for (int i = 1; i < algo_lib::_db.cmdline.verbose; i++) {
27871        ret << " -verbose";
27872    }
27873    for (int i = 1; i < algo_lib::_db.cmdline.debug; i++) {
27874        ret << " -debug";
27875    }
27876    return ret;
27877}
27878
27879// --- command.ssimfilt..PrintArgv
27880// print string representation of ROW to string STR
27881// cfmt:command.ssimfilt.Argv  printfmt:Tuple
27882void command::ssimfilt_PrintArgv(command::ssimfilt& row, algo::cstring& str) {
27883    algo::tempstr temp;
27884    (void)temp;
27885    (void)str;
27886    if (!(row.in == "data")) {
27887        ch_RemoveAll(temp);
27888        cstring_Print(row.in, temp);
27889        str << " -in:";
27890        strptr_PrintBash(temp,str);
27891    }
27892    ch_RemoveAll(temp);
27893    command::typetag_Print(const_cast<command::ssimfilt&>(row), temp);
27894    str << " -typetag:";
27895    strptr_PrintBash(temp,str);
27896    ind_beg(ssimfilt_match_curs,value,row) {
27897        ch_RemoveAll(temp);
27898        cstring_Print(value, temp);
27899        str << " -match:";
27900        strptr_PrintBash(temp,str);
27901    }ind_end;
27902    ind_beg(ssimfilt_field_curs,value,row) {
27903        ch_RemoveAll(temp);
27904        cstring_Print(value, temp);
27905        str << " -field:";
27906        strptr_PrintBash(temp,str);
27907    }ind_end;
27908    if (!(row.format == 0)) {
27909        ch_RemoveAll(temp);
27910        command::format_Print(const_cast<command::ssimfilt&>(row), temp);
27911        str << " -format:";
27912        strptr_PrintBash(temp,str);
27913    }
27914    if (!(row.t == false)) {
27915        ch_RemoveAll(temp);
27916        bool_Print(row.t, temp);
27917        str << " -t:";
27918        strptr_PrintBash(temp,str);
27919    }
27920    if (!(row.cmd == "")) {
27921        ch_RemoveAll(temp);
27922        cstring_Print(row.cmd, temp);
27923        str << " -cmd:";
27924        strptr_PrintBash(temp,str);
27925    }
27926}
27927
27928// --- command.ssimfilt..GetAnon
27929algo::strptr command::ssimfilt_GetAnon(command::ssimfilt &parent, i32 idx) {
27930    (void)parent;//only to avoid -Wunused-parameter
27931    switch(idx) {
27932        case(0): return strptr("typetag", 7);
27933        default: return strptr("match", 5);
27934    }
27935}
27936
27937// --- command.ssimfilt..NArgs
27938// Used with command lines
27939// Return # of command-line arguments that must follow this argument
27940// If FIELD is invalid, return -1
27941i32 command::ssimfilt_NArgs(command::FieldId field, algo::strptr& out_dflt, bool* out_anon) {
27942    i32 retval = 1;
27943    switch (field) {
27944        case command_FieldId_in: { // $comment
27945            *out_anon = false;
27946        } break;
27947        case command_FieldId_typetag: { // $comment
27948            *out_anon = true;
27949        } break;
27950        case command_FieldId_match: { // $comment
27951            *out_anon = true;
27952        } break;
27953        case command_FieldId_field: { // $comment
27954            *out_anon = false;
27955        } break;
27956        case command_FieldId_format: { // $comment
27957            *out_anon = false;
27958        } break;
27959        case command_FieldId_t: { // $comment
27960            *out_anon = false;
27961            retval=0;
27962            out_dflt="Y";
27963        } break;
27964        case command_FieldId_cmd: { // bool: no argument required but value may be specified as t:Y
27965            *out_anon = false;
27966        } break;
27967        default:
27968        retval=-1; // unrecognized
27969    }
27970    return retval;
27971}
27972
27973// --- command.ssimfilt_proc.ssimfilt.Start
27974// Start subprocess
27975// If subprocess already running, do nothing. Otherwise, start it
27976int command::ssimfilt_Start(command::ssimfilt_proc& parent) {
27977    int retval = 0;
27978    if (parent.pid == 0) {
27979        verblog(ssimfilt_ToCmdline(parent)); // maybe print command
27980#ifdef WIN32
27981        algo_lib::ResolveExecFname(parent.path);
27982        tempstr cmdline(ssimfilt_ToCmdline(parent));
27983        parent.pid = dospawn(Zeroterm(parent.path),Zeroterm(cmdline),parent.timeout,parent.fstdin,parent.fstdout,parent.fstderr);
27984#else
27985        parent.pid = fork();
27986        if (parent.pid == 0) { // child
27987            algo_lib::DieWithParent();
27988            if (parent.timeout > 0) {
27989                alarm(parent.timeout);
27990            }
27991            if (retval==0) retval=algo_lib::ApplyRedirect(parent.fstdin , 0);
27992            if (retval==0) retval=algo_lib::ApplyRedirect(parent.fstdout, 1);
27993            if (retval==0) retval=algo_lib::ApplyRedirect(parent.fstderr, 2);
27994            if (retval==0) retval= ssimfilt_Execv(parent);
27995            if (retval != 0) { // if start fails, print error
27996                int err=errno;
27997                prerr("command.ssimfilt_execv"
27998                <<Keyval("errno",err)
27999                <<Keyval("errstr",strerror(err))
28000                <<Keyval("comment","Execv failed"));
28001            }
28002            _exit(127); // if failed to start, exit anyway
28003        } else if (parent.pid == -1) {
28004            retval = errno; // failed to fork
28005        }
28006#endif
28007    }
28008    parent.status = parent.pid > 0 ? 0 : -1; // if didn't start, set error status
28009    return retval;
28010}
28011
28012// --- command.ssimfilt_proc.ssimfilt.StartRead
28013// Start subprocess & Read output
28014algo::Fildes command::ssimfilt_StartRead(command::ssimfilt_proc& parent, algo_lib::FFildes &read) {
28015    int pipefd[2];
28016    int rc=pipe(pipefd);
28017    (void)rc;
28018    read.fd.value = pipefd[0];
28019    parent.fstdout  << ">&" << pipefd[1];
28020    ssimfilt_Start(parent);
28021    (void)close(pipefd[1]);
28022    return read.fd;
28023}
28024
28025// --- command.ssimfilt_proc.ssimfilt.Kill
28026// Kill subprocess and wait
28027void command::ssimfilt_Kill(command::ssimfilt_proc& parent) {
28028    if (parent.pid != 0) {
28029        kill(parent.pid,9);
28030        ssimfilt_Wait(parent);
28031    }
28032}
28033
28034// --- command.ssimfilt_proc.ssimfilt.Wait
28035// Wait for subprocess to return
28036void command::ssimfilt_Wait(command::ssimfilt_proc& parent) {
28037    if (parent.pid > 0) {
28038        int wait_flags = 0;
28039        int wait_status = 0;
28040        int rc = -1;
28041        do {
28042            // really wait for subprocess to exit
28043            rc = waitpid(parent.pid,&wait_status,wait_flags);
28044        } while (rc==-1 && errno==EINTR);
28045        if (rc == parent.pid) {
28046            parent.status = wait_status;
28047            parent.pid = 0;
28048        }
28049    }
28050}
28051
28052// --- command.ssimfilt_proc.ssimfilt.Exec
28053// Start + Wait
28054// Execute subprocess and return exit code
28055int command::ssimfilt_Exec(command::ssimfilt_proc& parent) {
28056    ssimfilt_Start(parent);
28057    ssimfilt_Wait(parent);
28058    return parent.status;
28059}
28060
28061// --- command.ssimfilt_proc.ssimfilt.ExecX
28062// Start + Wait, throw exception on error
28063// Execute subprocess; throw human-readable exception on error
28064void command::ssimfilt_ExecX(command::ssimfilt_proc& parent) {
28065    int rc = ssimfilt_Exec(parent);
28066    vrfy(rc==0, tempstr() << "algo_lib.exec" << Keyval("cmd",ssimfilt_ToCmdline(parent))
28067    << Keyval("comment",algo::DescribeWaitStatus(parent.status)));
28068}
28069
28070// --- command.ssimfilt_proc.ssimfilt.Execv
28071// Call execv()
28072// Call execv with specified parameters
28073int command::ssimfilt_Execv(command::ssimfilt_proc& parent) {
28074    int ret = 0;
28075    algo::StringAry args;
28076    ssimfilt_ToArgv(parent, args);
28077    char **argv = (char**)alloca((ary_N(args)+1)*sizeof(*argv));
28078    ind_beg(algo::StringAry_ary_curs,arg,args) {
28079        argv[ind_curs(arg).index] = Zeroterm(arg);
28080    }ind_end;
28081    argv[ary_N(args)] = NULL;
28082    // if parent.path is relative, search for it in PATH
28083    algo_lib::ResolveExecFname(parent.path);
28084    ret = execv(Zeroterm(parent.path),argv);
28085    return ret;
28086}
28087
28088// --- command.ssimfilt_proc.ssimfilt.ToCmdline
28089algo::tempstr command::ssimfilt_ToCmdline(command::ssimfilt_proc& parent) {
28090    algo::tempstr retval;
28091    retval << parent.path << " ";
28092    command::ssimfilt_PrintArgv(parent.cmd,retval);
28093    if (ch_N(parent.fstdin)) {
28094        retval << " " << parent.fstdin;
28095    }
28096    if (ch_N(parent.fstdout)) {
28097        retval << " " << parent.fstdout;
28098    }
28099    if (ch_N(parent.fstderr)) {
28100        retval << " 2" << parent.fstderr;
28101    }
28102    return retval;
28103}
28104
28105// --- command.ssimfilt_proc.ssimfilt.ToArgv
28106// Form array from the command line
28107void command::ssimfilt_ToArgv(command::ssimfilt_proc& parent, algo::StringAry& args) {
28108    ary_RemoveAll(args);
28109    ary_Alloc(args) << parent.path;
28110
28111    if (parent.cmd.in != "data") {
28112        cstring *arg = &ary_Alloc(args);
28113        *arg << "-in:";
28114        cstring_Print(parent.cmd.in, *arg);
28115    }
28116
28117    if (parent.cmd.typetag.expr != "%") {
28118        cstring *arg = &ary_Alloc(args);
28119        *arg << "-typetag:";
28120        command::typetag_Print(parent.cmd, *arg);
28121    }
28122    ind_beg(command::ssimfilt_match_curs,value,parent.cmd) {
28123        cstring *arg = &ary_Alloc(args);
28124        *arg << "-match:";
28125        cstring_Print(value, *arg);
28126    }ind_end;
28127    ind_beg(command::ssimfilt_field_curs,value,parent.cmd) {
28128        cstring *arg = &ary_Alloc(args);
28129        *arg << "-field:";
28130        cstring_Print(value, *arg);
28131    }ind_end;
28132
28133    if (parent.cmd.format != 0) {
28134        cstring *arg = &ary_Alloc(args);
28135        *arg << "-format:";
28136        command::format_Print(parent.cmd, *arg);
28137    }
28138
28139    if (parent.cmd.t != false) {
28140        cstring *arg = &ary_Alloc(args);
28141        *arg << "-t:";
28142        bool_Print(parent.cmd.t, *arg);
28143    }
28144
28145    if (parent.cmd.cmd != "") {
28146        cstring *arg = &ary_Alloc(args);
28147        *arg << "-cmd:";
28148        cstring_Print(parent.cmd.cmd, *arg);
28149    }
28150    for (int i=1; i < algo_lib::_db.cmdline.verbose; ++i) {
28151        ary_Alloc(args) << "-verbose";
28152    }
28153}
28154
28155// --- command.ssimfilt_proc..Uninit
28156void command::ssimfilt_proc_Uninit(command::ssimfilt_proc& parent) {
28157    command::ssimfilt_proc &row = parent; (void)row;
28158
28159    // command.ssimfilt_proc.ssimfilt.Uninit (Exec)  //
28160    ssimfilt_Kill(parent); // kill child, ensure forward progress
28161}
28162
28163// --- command.strconv..ReadFieldMaybe
28164bool command::strconv_ReadFieldMaybe(command::strconv& parent, algo::strptr field, algo::strptr strval) {
28165    bool retval = true;
28166    command::FieldId field_id;
28167    (void)value_SetStrptrMaybe(field_id,field);
28168    switch(field_id) {
28169        case command_FieldId_str: {
28170            retval = algo::cstring_ReadStrptrMaybe(parent.str, strval);
28171            break;
28172        }
28173        case command_FieldId_tocamelcase: {
28174            retval = bool_ReadStrptrMaybe(parent.tocamelcase, strval);
28175            break;
28176        }
28177        case command_FieldId_tolowerunder: {
28178            retval = bool_ReadStrptrMaybe(parent.tolowerunder, strval);
28179            break;
28180        }
28181        case command_FieldId_in: {
28182            retval = algo::cstring_ReadStrptrMaybe(parent.in, strval);
28183            break;
28184        }
28185        case command_FieldId_pathcomp: {
28186            retval = algo::Smallstr100_ReadStrptrMaybe(parent.pathcomp, strval);
28187            break;
28188        }
28189        default: break;
28190    }
28191    if (!retval) {
28192        algo_lib::AppendErrtext("attr",field);
28193    }
28194    return retval;
28195}
28196
28197// --- command.strconv..ReadTupleMaybe
28198// Read fields of command::strconv from attributes of ascii tuple TUPLE
28199bool command::strconv_ReadTupleMaybe(command::strconv &parent, algo::Tuple &tuple) {
28200    bool retval = true;
28201    int anon_idx = 0;
28202    ind_beg(algo::Tuple_attrs_curs,attr,tuple) {
28203        if (ch_N(attr.name) == 0) {
28204            attr.name = strconv_GetAnon(parent, anon_idx++);
28205        }
28206        retval = strconv_ReadFieldMaybe(parent, attr.name, attr.value);
28207        if (!retval) {
28208            break;
28209        }
28210    }ind_end;
28211    return retval;
28212}
28213
28214// --- command.strconv..ToCmdline
28215// Convenience function that returns a full command line
28216// Assume command is in a directory called bin
28217tempstr command::strconv_ToCmdline(command::strconv& row) {
28218    tempstr ret;
28219    ret << "bin/strconv ";
28220    strconv_PrintArgv(row, ret);
28221    // inherit less intense verbose, debug options
28222    for (int i = 1; i < algo_lib::_db.cmdline.verbose; i++) {
28223        ret << " -verbose";
28224    }
28225    for (int i = 1; i < algo_lib::_db.cmdline.debug; i++) {
28226        ret << " -debug";
28227    }
28228    return ret;
28229}
28230
28231// --- command.strconv..PrintArgv
28232// print string representation of ROW to string STR
28233// cfmt:command.strconv.Argv  printfmt:Tuple
28234void command::strconv_PrintArgv(command::strconv& row, algo::cstring& str) {
28235    algo::tempstr temp;
28236    (void)temp;
28237    (void)str;
28238    ch_RemoveAll(temp);
28239    cstring_Print(row.str, temp);
28240    str << " -str:";
28241    strptr_PrintBash(temp,str);
28242    if (!(row.tocamelcase == false)) {
28243        ch_RemoveAll(temp);
28244        bool_Print(row.tocamelcase, temp);
28245        str << " -tocamelcase:";
28246        strptr_PrintBash(temp,str);
28247    }
28248    if (!(row.tolowerunder == false)) {
28249        ch_RemoveAll(temp);
28250        bool_Print(row.tolowerunder, temp);
28251        str << " -tolowerunder:";
28252        strptr_PrintBash(temp,str);
28253    }
28254    if (!(row.in == "data")) {
28255        ch_RemoveAll(temp);
28256        cstring_Print(row.in, temp);
28257        str << " -in:";
28258        strptr_PrintBash(temp,str);
28259    }
28260    if (!(row.pathcomp == "")) {
28261        ch_RemoveAll(temp);
28262        Smallstr100_Print(row.pathcomp, temp);
28263        str << " -pathcomp:";
28264        strptr_PrintBash(temp,str);
28265    }
28266}
28267
28268// --- command.strconv..GetAnon
28269algo::strptr command::strconv_GetAnon(command::strconv &parent, i32 idx) {
28270    (void)parent;//only to avoid -Wunused-parameter
28271    switch(idx) {
28272        case(0): return strptr("str", 3);
28273        default: return algo::strptr();
28274    }
28275}
28276
28277// --- command.strconv..NArgs
28278// Used with command lines
28279// Return # of command-line arguments that must follow this argument
28280// If FIELD is invalid, return -1
28281i32 command::strconv_NArgs(command::FieldId field, algo::strptr& out_dflt, bool* out_anon) {
28282    i32 retval = 1;
28283    switch (field) {
28284        case command_FieldId_str: { // $comment
28285            *out_anon = true;
28286        } break;
28287        case command_FieldId_tocamelcase: { // $comment
28288            *out_anon = false;
28289            retval=0;
28290            out_dflt="Y";
28291        } break;
28292        case command_FieldId_tolowerunder: { // bool: no argument required but value may be specified as tocamelcase:Y
28293            *out_anon = false;
28294            retval=0;
28295            out_dflt="Y";
28296        } break;
28297        case command_FieldId_in: { // bool: no argument required but value may be specified as tolowerunder:Y
28298            *out_anon = false;
28299        } break;
28300        case command_FieldId_pathcomp: { // bool: no argument required but value may be specified as tolowerunder:Y
28301            *out_anon = false;
28302        } break;
28303        default:
28304        retval=-1; // unrecognized
28305    }
28306    return retval;
28307}
28308
28309// --- command.strconv_proc.strconv.Start
28310// Start subprocess
28311// If subprocess already running, do nothing. Otherwise, start it
28312int command::strconv_Start(command::strconv_proc& parent) {
28313    int retval = 0;
28314    if (parent.pid == 0) {
28315        verblog(strconv_ToCmdline(parent)); // maybe print command
28316#ifdef WIN32
28317        algo_lib::ResolveExecFname(parent.path);
28318        tempstr cmdline(strconv_ToCmdline(parent));
28319        parent.pid = dospawn(Zeroterm(parent.path),Zeroterm(cmdline),parent.timeout,parent.fstdin,parent.fstdout,parent.fstderr);
28320#else
28321        parent.pid = fork();
28322        if (parent.pid == 0) { // child
28323            algo_lib::DieWithParent();
28324            if (parent.timeout > 0) {
28325                alarm(parent.timeout);
28326            }
28327            if (retval==0) retval=algo_lib::ApplyRedirect(parent.fstdin , 0);
28328            if (retval==0) retval=algo_lib::ApplyRedirect(parent.fstdout, 1);
28329            if (retval==0) retval=algo_lib::ApplyRedirect(parent.fstderr, 2);
28330            if (retval==0) retval= strconv_Execv(parent);
28331            if (retval != 0) { // if start fails, print error
28332                int err=errno;
28333                prerr("command.strconv_execv"
28334                <<Keyval("errno",err)
28335                <<Keyval("errstr",strerror(err))
28336                <<Keyval("comment","Execv failed"));
28337            }
28338            _exit(127); // if failed to start, exit anyway
28339        } else if (parent.pid == -1) {
28340            retval = errno; // failed to fork
28341        }
28342#endif
28343    }
28344    parent.status = parent.pid > 0 ? 0 : -1; // if didn't start, set error status
28345    return retval;
28346}
28347
28348// --- command.strconv_proc.strconv.StartRead
28349// Start subprocess & Read output
28350algo::Fildes command::strconv_StartRead(command::strconv_proc& parent, algo_lib::FFildes &read) {
28351    int pipefd[2];
28352    int rc=pipe(pipefd);
28353    (void)rc;
28354    read.fd.value = pipefd[0];
28355    parent.fstdout  << ">&" << pipefd[1];
28356    strconv_Start(parent);
28357    (void)close(pipefd[1]);
28358    return read.fd;
28359}
28360
28361// --- command.strconv_proc.strconv.Kill
28362// Kill subprocess and wait
28363void command::strconv_Kill(command::strconv_proc& parent) {
28364    if (parent.pid != 0) {
28365        kill(parent.pid,9);
28366        strconv_Wait(parent);
28367    }
28368}
28369
28370// --- command.strconv_proc.strconv.Wait
28371// Wait for subprocess to return
28372void command::strconv_Wait(command::strconv_proc& parent) {
28373    if (parent.pid > 0) {
28374        int wait_flags = 0;
28375        int wait_status = 0;
28376        int rc = -1;
28377        do {
28378            // really wait for subprocess to exit
28379            rc = waitpid(parent.pid,&wait_status,wait_flags);
28380        } while (rc==-1 && errno==EINTR);
28381        if (rc == parent.pid) {
28382            parent.status = wait_status;
28383            parent.pid = 0;
28384        }
28385    }
28386}
28387
28388// --- command.strconv_proc.strconv.Exec
28389// Start + Wait
28390// Execute subprocess and return exit code
28391int command::strconv_Exec(command::strconv_proc& parent) {
28392    strconv_Start(parent);
28393    strconv_Wait(parent);
28394    return parent.status;
28395}
28396
28397// --- command.strconv_proc.strconv.ExecX
28398// Start + Wait, throw exception on error
28399// Execute subprocess; throw human-readable exception on error
28400void command::strconv_ExecX(command::strconv_proc& parent) {
28401    int rc = strconv_Exec(parent);
28402    vrfy(rc==0, tempstr() << "algo_lib.exec" << Keyval("cmd",strconv_ToCmdline(parent))
28403    << Keyval("comment",algo::DescribeWaitStatus(parent.status)));
28404}
28405
28406// --- command.strconv_proc.strconv.Execv
28407// Call execv()
28408// Call execv with specified parameters
28409int command::strconv_Execv(command::strconv_proc& parent) {
28410    int ret = 0;
28411    algo::StringAry args;
28412    strconv_ToArgv(parent, args);
28413    char **argv = (char**)alloca((ary_N(args)+1)*sizeof(*argv));
28414    ind_beg(algo::StringAry_ary_curs,arg,args) {
28415        argv[ind_curs(arg).index] = Zeroterm(arg);
28416    }ind_end;
28417    argv[ary_N(args)] = NULL;
28418    // if parent.path is relative, search for it in PATH
28419    algo_lib::ResolveExecFname(parent.path);
28420    ret = execv(Zeroterm(parent.path),argv);
28421    return ret;
28422}
28423
28424// --- command.strconv_proc.strconv.ToCmdline
28425algo::tempstr command::strconv_ToCmdline(command::strconv_proc& parent) {
28426    algo::tempstr retval;
28427    retval << parent.path << " ";
28428    command::strconv_PrintArgv(parent.cmd,retval);
28429    if (ch_N(parent.fstdin)) {
28430        retval << " " << parent.fstdin;
28431    }
28432    if (ch_N(parent.fstdout)) {
28433        retval << " " << parent.fstdout;
28434    }
28435    if (ch_N(parent.fstderr)) {
28436        retval << " 2" << parent.fstderr;
28437    }
28438    return retval;
28439}
28440
28441// --- command.strconv_proc.strconv.ToArgv
28442// Form array from the command line
28443void command::strconv_ToArgv(command::strconv_proc& parent, algo::StringAry& args) {
28444    ary_RemoveAll(args);
28445    ary_Alloc(args) << parent.path;
28446
28447    if (true) {
28448        cstring *arg = &ary_Alloc(args);
28449        *arg << "-str:";
28450        cstring_Print(parent.cmd.str, *arg);
28451    }
28452
28453    if (parent.cmd.tocamelcase != false) {
28454        cstring *arg = &ary_Alloc(args);
28455        *arg << "-tocamelcase:";
28456        bool_Print(parent.cmd.tocamelcase, *arg);
28457    }
28458
28459    if (parent.cmd.tolowerunder != false) {
28460        cstring *arg = &ary_Alloc(args);
28461        *arg << "-tolowerunder:";
28462        bool_Print(parent.cmd.tolowerunder, *arg);
28463    }
28464
28465    if (parent.cmd.in != "data") {
28466        cstring *arg = &ary_Alloc(args);
28467        *arg << "-in:";
28468        cstring_Print(parent.cmd.in, *arg);
28469    }
28470
28471    if (parent.cmd.pathcomp != "") {
28472        cstring *arg = &ary_Alloc(args);
28473        *arg << "-pathcomp:";
28474        Smallstr100_Print(parent.cmd.pathcomp, *arg);
28475    }
28476    for (int i=1; i < algo_lib::_db.cmdline.verbose; ++i) {
28477        ary_Alloc(args) << "-verbose";
28478    }
28479}
28480
28481// --- command.strconv_proc..Uninit
28482void command::strconv_proc_Uninit(command::strconv_proc& parent) {
28483    command::strconv_proc &row = parent; (void)row;
28484
28485    // command.strconv_proc.strconv.Uninit (Exec)  //
28486    strconv_Kill(parent); // kill child, ensure forward progress
28487}
28488
28489// --- command.sv2ssim.field.Print
28490// Print back to string
28491void command::field_Print(command::sv2ssim& parent, algo::cstring &out) {
28492    Regx_Print(parent.field, out);
28493}
28494
28495// --- command.sv2ssim.field.ReadStrptrMaybe
28496// Read Regx from string
28497// Convert string to field. Return success value
28498bool command::field_ReadStrptrMaybe(command::sv2ssim& parent, algo::strptr in) {
28499    bool retval = true;
28500    Regx_ReadSql(parent.field, in, true);
28501    return retval;
28502}
28503
28504// --- command.sv2ssim..ReadFieldMaybe
28505bool command::sv2ssim_ReadFieldMaybe(command::sv2ssim& parent, algo::strptr field, algo::strptr strval) {
28506    bool retval = true;
28507    command::FieldId field_id;
28508    (void)value_SetStrptrMaybe(field_id,field);
28509    switch(field_id) {
28510        case command_FieldId_in: {
28511            retval = algo::cstring_ReadStrptrMaybe(parent.in, strval);
28512            break;
28513        }
28514        case command_FieldId_fname: {
28515            retval = algo::cstring_ReadStrptrMaybe(parent.fname, strval);
28516            break;
28517        }
28518        case command_FieldId_separator: {
28519            retval = char_ReadStrptrMaybe(parent.separator, strval);
28520            break;
28521        }
28522        case command_FieldId_outseparator: {
28523            retval = algo::cstring_ReadStrptrMaybe(parent.outseparator, strval);
28524            break;
28525        }
28526        case command_FieldId_header: {
28527            retval = bool_ReadStrptrMaybe(parent.header, strval);
28528            break;
28529        }
28530        case command_FieldId_ctype: {
28531            retval = algo::cstring_ReadStrptrMaybe(parent.ctype, strval);
28532            break;
28533        }
28534        case command_FieldId_ssimfile: {
28535            retval = algo::cstring_ReadStrptrMaybe(parent.ssimfile, strval);
28536            break;
28537        }
28538        case command_FieldId_schema: {
28539            retval = bool_ReadStrptrMaybe(parent.schema, strval);
28540            break;
28541        }
28542        case command_FieldId_field: {
28543            retval = field_ReadStrptrMaybe(parent, strval);
28544            break;
28545        }
28546        case command_FieldId_data: {
28547            retval = bool_ReadStrptrMaybe(parent.data, strval);
28548            break;
28549        }
28550        case command_FieldId_report: {
28551            retval = bool_ReadStrptrMaybe(parent.report, strval);
28552            break;
28553        }
28554        case command_FieldId_prefer_signed: {
28555            retval = bool_ReadStrptrMaybe(parent.prefer_signed, strval);
28556            break;
28557        }
28558        default: break;
28559    }
28560    if (!retval) {
28561        algo_lib::AppendErrtext("attr",field);
28562    }
28563    return retval;
28564}
28565
28566// --- command.sv2ssim..ReadTupleMaybe
28567// Read fields of command::sv2ssim from attributes of ascii tuple TUPLE
28568bool command::sv2ssim_ReadTupleMaybe(command::sv2ssim &parent, algo::Tuple &tuple) {
28569    bool retval = true;
28570    int anon_idx = 0;
28571    ind_beg(algo::Tuple_attrs_curs,attr,tuple) {
28572        if (ch_N(attr.name) == 0) {
28573            attr.name = sv2ssim_GetAnon(parent, anon_idx++);
28574        }
28575        retval = sv2ssim_ReadFieldMaybe(parent, attr.name, attr.value);
28576        if (!retval) {
28577            break;
28578        }
28579    }ind_end;
28580    return retval;
28581}
28582
28583// --- command.sv2ssim..Init
28584// Set all fields to initial values.
28585void command::sv2ssim_Init(command::sv2ssim& parent) {
28586    parent.in = algo::strptr("data");
28587    parent.separator = char(',');
28588    parent.outseparator = algo::strptr("");
28589    parent.header = bool(true);
28590    parent.ctype = algo::strptr("");
28591    parent.ssimfile = algo::strptr("");
28592    parent.schema = bool(false);
28593    Regx_ReadSql(parent.field, "%", true);
28594    parent.data = bool(false);
28595    parent.report = bool(true);
28596    parent.prefer_signed = bool(false);
28597}
28598
28599// --- command.sv2ssim..ToCmdline
28600// Convenience function that returns a full command line
28601// Assume command is in a directory called bin
28602tempstr command::sv2ssim_ToCmdline(command::sv2ssim& row) {
28603    tempstr ret;
28604    ret << "bin/sv2ssim ";
28605    sv2ssim_PrintArgv(row, ret);
28606    // inherit less intense verbose, debug options
28607    for (int i = 1; i < algo_lib::_db.cmdline.verbose; i++) {
28608        ret << " -verbose";
28609    }
28610    for (int i = 1; i < algo_lib::_db.cmdline.debug; i++) {
28611        ret << " -debug";
28612    }
28613    return ret;
28614}
28615
28616// --- command.sv2ssim..PrintArgv
28617// print string representation of ROW to string STR
28618// cfmt:command.sv2ssim.Argv  printfmt:Tuple
28619void command::sv2ssim_PrintArgv(command::sv2ssim& row, algo::cstring& str) {
28620    algo::tempstr temp;
28621    (void)temp;
28622    (void)str;
28623    if (!(row.in == "data")) {
28624        ch_RemoveAll(temp);
28625        cstring_Print(row.in, temp);
28626        str << " -in:";
28627        strptr_PrintBash(temp,str);
28628    }
28629    ch_RemoveAll(temp);
28630    cstring_Print(row.fname, temp);
28631    str << " -fname:";
28632    strptr_PrintBash(temp,str);
28633    if (!(row.separator == ',')) {
28634        ch_RemoveAll(temp);
28635        char_Print(row.separator, temp);
28636        str << " -separator:";
28637        strptr_PrintBash(temp,str);
28638    }
28639    if (!(row.outseparator == "")) {
28640        ch_RemoveAll(temp);
28641        cstring_Print(row.outseparator, temp);
28642        str << " -outseparator:";
28643        strptr_PrintBash(temp,str);
28644    }
28645    if (!(row.header == true)) {
28646        ch_RemoveAll(temp);
28647        bool_Print(row.header, temp);
28648        str << " -header:";
28649        strptr_PrintBash(temp,str);
28650    }
28651    if (!(row.ctype == "")) {
28652        ch_RemoveAll(temp);
28653        cstring_Print(row.ctype, temp);
28654        str << " -ctype:";
28655        strptr_PrintBash(temp,str);
28656    }
28657    if (!(row.ssimfile == "")) {
28658        ch_RemoveAll(temp);
28659        cstring_Print(row.ssimfile, temp);
28660        str << " -ssimfile:";
28661        strptr_PrintBash(temp,str);
28662    }
28663    if (!(row.schema == false)) {
28664        ch_RemoveAll(temp);
28665        bool_Print(row.schema, temp);
28666        str << " -schema:";
28667        strptr_PrintBash(temp,str);
28668    }
28669    if (!(row.field.expr == "%")) {
28670        ch_RemoveAll(temp);
28671        command::field_Print(const_cast<command::sv2ssim&>(row), temp);
28672        str << " -field:";
28673        strptr_PrintBash(temp,str);
28674    }
28675    if (!(row.data == false)) {
28676        ch_RemoveAll(temp);
28677        bool_Print(row.data, temp);
28678        str << " -data:";
28679        strptr_PrintBash(temp,str);
28680    }
28681    if (!(row.report == true)) {
28682        ch_RemoveAll(temp);
28683        bool_Print(row.report, temp);
28684        str << " -report:";
28685        strptr_PrintBash(temp,str);
28686    }
28687    if (!(row.prefer_signed == false)) {
28688        ch_RemoveAll(temp);
28689        bool_Print(row.prefer_signed, temp);
28690        str << " -prefer_signed:";
28691        strptr_PrintBash(temp,str);
28692    }
28693}
28694
28695// --- command.sv2ssim..GetAnon
28696algo::strptr command::sv2ssim_GetAnon(command::sv2ssim &parent, i32 idx) {
28697    (void)parent;//only to avoid -Wunused-parameter
28698    switch(idx) {
28699        case(0): return strptr("fname", 5);
28700        default: return algo::strptr();
28701    }
28702}
28703
28704// --- command.sv2ssim..NArgs
28705// Used with command lines
28706// Return # of command-line arguments that must follow this argument
28707// If FIELD is invalid, return -1
28708i32 command::sv2ssim_NArgs(command::FieldId field, algo::strptr& out_dflt, bool* out_anon) {
28709    i32 retval = 1;
28710    switch (field) {
28711        case command_FieldId_in: { // $comment
28712            *out_anon = false;
28713        } break;
28714        case command_FieldId_fname: { // $comment
28715            *out_anon = true;
28716        } break;
28717        case command_FieldId_separator: { // $comment
28718            *out_anon = false;
28719        } break;
28720        case command_FieldId_outseparator: { // $comment
28721            *out_anon = false;
28722        } break;
28723        case command_FieldId_header: { // $comment
28724            *out_anon = false;
28725            retval=0;
28726            out_dflt="Y";
28727        } break;
28728        case command_FieldId_ctype: { // bool: no argument required but value may be specified as header:Y
28729            *out_anon = false;
28730        } break;
28731        case command_FieldId_ssimfile: { // bool: no argument required but value may be specified as header:Y
28732            *out_anon = false;
28733        } break;
28734        case command_FieldId_schema: { // bool: no argument required but value may be specified as header:Y
28735            *out_anon = false;
28736            retval=0;
28737            out_dflt="Y";
28738        } break;
28739        case command_FieldId_field: { // bool: no argument required but value may be specified as schema:Y
28740            *out_anon = false;
28741        } break;
28742        case command_FieldId_data: { // bool: no argument required but value may be specified as schema:Y
28743            *out_anon = false;
28744            retval=0;
28745            out_dflt="Y";
28746        } break;
28747        case command_FieldId_report: { // bool: no argument required but value may be specified as data:Y
28748            *out_anon = false;
28749            retval=0;
28750            out_dflt="Y";
28751        } break;
28752        case command_FieldId_prefer_signed: { // bool: no argument required but value may be specified as report:Y
28753            *out_anon = false;
28754            retval=0;
28755            out_dflt="Y";
28756        } break;
28757        default:
28758        retval=-1; // unrecognized
28759    }
28760    return retval;
28761}
28762
28763// --- command.sv2ssim_proc.sv2ssim.Start
28764// Start subprocess
28765// If subprocess already running, do nothing. Otherwise, start it
28766int command::sv2ssim_Start(command::sv2ssim_proc& parent) {
28767    int retval = 0;
28768    if (parent.pid == 0) {
28769        verblog(sv2ssim_ToCmdline(parent)); // maybe print command
28770#ifdef WIN32
28771        algo_lib::ResolveExecFname(parent.path);
28772        tempstr cmdline(sv2ssim_ToCmdline(parent));
28773        parent.pid = dospawn(Zeroterm(parent.path),Zeroterm(cmdline),parent.timeout,parent.fstdin,parent.fstdout,parent.fstderr);
28774#else
28775        parent.pid = fork();
28776        if (parent.pid == 0) { // child
28777            algo_lib::DieWithParent();
28778            if (parent.timeout > 0) {
28779                alarm(parent.timeout);
28780            }
28781            if (retval==0) retval=algo_lib::ApplyRedirect(parent.fstdin , 0);
28782            if (retval==0) retval=algo_lib::ApplyRedirect(parent.fstdout, 1);
28783            if (retval==0) retval=algo_lib::ApplyRedirect(parent.fstderr, 2);
28784            if (retval==0) retval= sv2ssim_Execv(parent);
28785            if (retval != 0) { // if start fails, print error
28786                int err=errno;
28787                prerr("command.sv2ssim_execv"
28788                <<Keyval("errno",err)
28789                <<Keyval("errstr",strerror(err))
28790                <<Keyval("comment","Execv failed"));
28791            }
28792            _exit(127); // if failed to start, exit anyway
28793        } else if (parent.pid == -1) {
28794            retval = errno; // failed to fork
28795        }
28796#endif
28797    }
28798    parent.status = parent.pid > 0 ? 0 : -1; // if didn't start, set error status
28799    return retval;
28800}
28801
28802// --- command.sv2ssim_proc.sv2ssim.StartRead
28803// Start subprocess & Read output
28804algo::Fildes command::sv2ssim_StartRead(command::sv2ssim_proc& parent, algo_lib::FFildes &read) {
28805    int pipefd[2];
28806    int rc=pipe(pipefd);
28807    (void)rc;
28808    read.fd.value = pipefd[0];
28809    parent.fstdout  << ">&" << pipefd[1];
28810    sv2ssim_Start(parent);
28811    (void)close(pipefd[1]);
28812    return read.fd;
28813}
28814
28815// --- command.sv2ssim_proc.sv2ssim.Kill
28816// Kill subprocess and wait
28817void command::sv2ssim_Kill(command::sv2ssim_proc& parent) {
28818    if (parent.pid != 0) {
28819        kill(parent.pid,9);
28820        sv2ssim_Wait(parent);
28821    }
28822}
28823
28824// --- command.sv2ssim_proc.sv2ssim.Wait
28825// Wait for subprocess to return
28826void command::sv2ssim_Wait(command::sv2ssim_proc& parent) {
28827    if (parent.pid > 0) {
28828        int wait_flags = 0;
28829        int wait_status = 0;
28830        int rc = -1;
28831        do {
28832            // really wait for subprocess to exit
28833            rc = waitpid(parent.pid,&wait_status,wait_flags);
28834        } while (rc==-1 && errno==EINTR);
28835        if (rc == parent.pid) {
28836            parent.status = wait_status;
28837            parent.pid = 0;
28838        }
28839    }
28840}
28841
28842// --- command.sv2ssim_proc.sv2ssim.Exec
28843// Start + Wait
28844// Execute subprocess and return exit code
28845int command::sv2ssim_Exec(command::sv2ssim_proc& parent) {
28846    sv2ssim_Start(parent);
28847    sv2ssim_Wait(parent);
28848    return parent.status;
28849}
28850
28851// --- command.sv2ssim_proc.sv2ssim.ExecX
28852// Start + Wait, throw exception on error
28853// Execute subprocess; throw human-readable exception on error
28854void command::sv2ssim_ExecX(command::sv2ssim_proc& parent) {
28855    int rc = sv2ssim_Exec(parent);
28856    vrfy(rc==0, tempstr() << "algo_lib.exec" << Keyval("cmd",sv2ssim_ToCmdline(parent))
28857    << Keyval("comment",algo::DescribeWaitStatus(parent.status)));
28858}
28859
28860// --- command.sv2ssim_proc.sv2ssim.Execv
28861// Call execv()
28862// Call execv with specified parameters
28863int command::sv2ssim_Execv(command::sv2ssim_proc& parent) {
28864    int ret = 0;
28865    algo::StringAry args;
28866    sv2ssim_ToArgv(parent, args);
28867    char **argv = (char**)alloca((ary_N(args)+1)*sizeof(*argv));
28868    ind_beg(algo::StringAry_ary_curs,arg,args) {
28869        argv[ind_curs(arg).index] = Zeroterm(arg);
28870    }ind_end;
28871    argv[ary_N(args)] = NULL;
28872    // if parent.path is relative, search for it in PATH
28873    algo_lib::ResolveExecFname(parent.path);
28874    ret = execv(Zeroterm(parent.path),argv);
28875    return ret;
28876}
28877
28878// --- command.sv2ssim_proc.sv2ssim.ToCmdline
28879algo::tempstr command::sv2ssim_ToCmdline(command::sv2ssim_proc& parent) {
28880    algo::tempstr retval;
28881    retval << parent.path << " ";
28882    command::sv2ssim_PrintArgv(parent.cmd,retval);
28883    if (ch_N(parent.fstdin)) {
28884        retval << " " << parent.fstdin;
28885    }
28886    if (ch_N(parent.fstdout)) {
28887        retval << " " << parent.fstdout;
28888    }
28889    if (ch_N(parent.fstderr)) {
28890        retval << " 2" << parent.fstderr;
28891    }
28892    return retval;
28893}
28894
28895// --- command.sv2ssim_proc.sv2ssim.ToArgv
28896// Form array from the command line
28897void command::sv2ssim_ToArgv(command::sv2ssim_proc& parent, algo::StringAry& args) {
28898    ary_RemoveAll(args);
28899    ary_Alloc(args) << parent.path;
28900
28901    if (parent.cmd.in != "data") {
28902        cstring *arg = &ary_Alloc(args);
28903        *arg << "-in:";
28904        cstring_Print(parent.cmd.in, *arg);
28905    }
28906
28907    if (true) {
28908        cstring *arg = &ary_Alloc(args);
28909        *arg << "-fname:";
28910        cstring_Print(parent.cmd.fname, *arg);
28911    }
28912
28913    if (parent.cmd.separator != ',') {
28914        cstring *arg = &ary_Alloc(args);
28915        *arg << "-separator:";
28916        char_Print(parent.cmd.separator, *arg);
28917    }
28918
28919    if (parent.cmd.outseparator != "") {
28920        cstring *arg = &ary_Alloc(args);
28921        *arg << "-outseparator:";
28922        cstring_Print(parent.cmd.outseparator, *arg);
28923    }
28924
28925    if (parent.cmd.header != true) {
28926        cstring *arg = &ary_Alloc(args);
28927        *arg << "-header:";
28928        bool_Print(parent.cmd.header, *arg);
28929    }
28930
28931    if (parent.cmd.ctype != "") {
28932        cstring *arg = &ary_Alloc(args);
28933        *arg << "-ctype:";
28934        cstring_Print(parent.cmd.ctype, *arg);
28935    }
28936
28937    if (parent.cmd.ssimfile != "") {
28938        cstring *arg = &ary_Alloc(args);
28939        *arg << "-ssimfile:";
28940        cstring_Print(parent.cmd.ssimfile, *arg);
28941    }
28942
28943    if (parent.cmd.schema != false) {
28944        cstring *arg = &ary_Alloc(args);
28945        *arg << "-schema:";
28946        bool_Print(parent.cmd.schema, *arg);
28947    }
28948
28949    if (parent.cmd.field.expr != "%") {
28950        cstring *arg = &ary_Alloc(args);
28951        *arg << "-field:";
28952        command::field_Print(parent.cmd, *arg);
28953    }
28954
28955    if (parent.cmd.data != false) {
28956        cstring *arg = &ary_Alloc(args);
28957        *arg << "-data:";
28958        bool_Print(parent.cmd.data, *arg);
28959    }
28960
28961    if (parent.cmd.report != true) {
28962        cstring *arg = &ary_Alloc(args);
28963        *arg << "-report:";
28964        bool_Print(parent.cmd.report, *arg);
28965    }
28966
28967    if (parent.cmd.prefer_signed != false) {
28968        cstring *arg = &ary_Alloc(args);
28969        *arg << "-prefer_signed:";
28970        bool_Print(parent.cmd.prefer_signed, *arg);
28971    }
28972    for (int i=1; i < algo_lib::_db.cmdline.verbose; ++i) {
28973        ary_Alloc(args) << "-verbose";
28974    }
28975}
28976
28977// --- command.sv2ssim_proc..Uninit
28978void command::sv2ssim_proc_Uninit(command::sv2ssim_proc& parent) {
28979    command::sv2ssim_proc &row = parent; (void)row;
28980
28981    // command.sv2ssim_proc.sv2ssim.Uninit (Exec)  //
28982    sv2ssim_Kill(parent); // kill child, ensure forward progress
28983}
28984
28985// --- command.ttrack..ReadFieldMaybe
28986bool command::ttrack_ReadFieldMaybe(command::ttrack& parent, algo::strptr field, algo::strptr strval) {
28987    bool retval = true;
28988    command::FieldId field_id;
28989    (void)value_SetStrptrMaybe(field_id,field);
28990    switch(field_id) {
28991        case command_FieldId_in: {
28992            retval = algo::cstring_ReadStrptrMaybe(parent.in, strval);
28993            break;
28994        }
28995        case command_FieldId_begin: {
28996            retval = bool_ReadStrptrMaybe(parent.begin, strval);
28997            break;
28998        }
28999        case command_FieldId_end: {
29000            retval = bool_ReadStrptrMaybe(parent.end, strval);
29001            break;
29002        }
29003        case command_FieldId_stay: {
29004            retval = bool_ReadStrptrMaybe(parent.stay, strval);
29005            break;
29006        }
29007        case command_FieldId_logfile: {
29008            retval = algo::cstring_ReadStrptrMaybe(parent.logfile, strval);
29009            break;
29010        }
29011        case command_FieldId_report: {
29012            retval = bool_ReadStrptrMaybe(parent.report, strval);
29013            break;
29014        }
29015        case command_FieldId_from: {
29016            retval = algo::UnTime_ReadStrptrMaybe(parent.from, strval);
29017            break;
29018        }
29019        case command_FieldId_to: {
29020            retval = algo::UnTime_ReadStrptrMaybe(parent.to, strval);
29021            break;
29022        }
29023        case command_FieldId_e: {
29024            retval = bool_ReadStrptrMaybe(parent.e, strval);
29025            break;
29026        }
29027        case command_FieldId_comment: {
29028            retval = algo::cstring_ReadStrptrMaybe(parent.comment, strval);
29029            break;
29030        }
29031        default: break;
29032    }
29033    if (!retval) {
29034        algo_lib::AppendErrtext("attr",field);
29035    }
29036    return retval;
29037}
29038
29039// --- command.ttrack..ReadTupleMaybe
29040// Read fields of command::ttrack from attributes of ascii tuple TUPLE
29041bool command::ttrack_ReadTupleMaybe(command::ttrack &parent, algo::Tuple &tuple) {
29042    bool retval = true;
29043    ind_beg(algo::Tuple_attrs_curs,attr,tuple) {
29044        retval = ttrack_ReadFieldMaybe(parent, attr.name, attr.value);
29045        if (!retval) {
29046            break;
29047        }
29048    }ind_end;
29049    return retval;
29050}
29051
29052// --- command.ttrack..Init
29053// Set all fields to initial values.
29054void command::ttrack_Init(command::ttrack& parent) {
29055    parent.in = algo::strptr("data");
29056    parent.begin = bool(false);
29057    parent.end = bool(false);
29058    parent.stay = bool(false);
29059    parent.logfile = algo::strptr("~/.ssim/ttrack.ssim");
29060    parent.report = bool(true);
29061    parent.e = bool(false);
29062    parent.comment = algo::strptr("");
29063}
29064
29065// --- command.ttrack..ToCmdline
29066// Convenience function that returns a full command line
29067// Assume command is in a directory called bin
29068tempstr command::ttrack_ToCmdline(command::ttrack& row) {
29069    tempstr ret;
29070    ret << "bin/ttrack ";
29071    ttrack_PrintArgv(row, ret);
29072    // inherit less intense verbose, debug options
29073    for (int i = 1; i < algo_lib::_db.cmdline.verbose; i++) {
29074        ret << " -verbose";
29075    }
29076    for (int i = 1; i < algo_lib::_db.cmdline.debug; i++) {
29077        ret << " -debug";
29078    }
29079    return ret;
29080}
29081
29082// --- command.ttrack..PrintArgv
29083// print string representation of ROW to string STR
29084// cfmt:command.ttrack.Argv  printfmt:Tuple
29085void command::ttrack_PrintArgv(command::ttrack& row, algo::cstring& str) {
29086    algo::tempstr temp;
29087    (void)temp;
29088    (void)str;
29089    if (!(row.in == "data")) {
29090        ch_RemoveAll(temp);
29091        cstring_Print(row.in, temp);
29092        str << " -in:";
29093        strptr_PrintBash(temp,str);
29094    }
29095    if (!(row.begin == false)) {
29096        ch_RemoveAll(temp);
29097        bool_Print(row.begin, temp);
29098        str << " -begin:";
29099        strptr_PrintBash(temp,str);
29100    }
29101    if (!(row.end == false)) {
29102        ch_RemoveAll(temp);
29103        bool_Print(row.end, temp);
29104        str << " -end:";
29105        strptr_PrintBash(temp,str);
29106    }
29107    if (!(row.stay == false)) {
29108        ch_RemoveAll(temp);
29109        bool_Print(row.stay, temp);
29110        str << " -stay:";
29111        strptr_PrintBash(temp,str);
29112    }
29113    if (!(row.logfile == "~/.ssim/ttrack.ssim")) {
29114        ch_RemoveAll(temp);
29115        cstring_Print(row.logfile, temp);
29116        str << " -logfile:";
29117        strptr_PrintBash(temp,str);
29118    }
29119    if (!(row.report == true)) {
29120        ch_RemoveAll(temp);
29121        bool_Print(row.report, temp);
29122        str << " -report:";
29123        strptr_PrintBash(temp,str);
29124    }
29125    if (!(UnTime_Eq(row.from, algo::UnTime()))) {
29126        ch_RemoveAll(temp);
29127        UnTime_Print(row.from, temp);
29128        str << " -from:";
29129        strptr_PrintBash(temp,str);
29130    }
29131    if (!(UnTime_Eq(row.to, algo::UnTime()))) {
29132        ch_RemoveAll(temp);
29133        UnTime_Print(row.to, temp);
29134        str << " -to:";
29135        strptr_PrintBash(temp,str);
29136    }
29137    if (!(row.e == false)) {
29138        ch_RemoveAll(temp);
29139        bool_Print(row.e, temp);
29140        str << " -e:";
29141        strptr_PrintBash(temp,str);
29142    }
29143    if (!(row.comment == "")) {
29144        ch_RemoveAll(temp);
29145        cstring_Print(row.comment, temp);
29146        str << " -comment:";
29147        strptr_PrintBash(temp,str);
29148    }
29149}
29150
29151// --- command.ttrack..NArgs
29152// Used with command lines
29153// Return # of command-line arguments that must follow this argument
29154// If FIELD is invalid, return -1
29155i32 command::ttrack_NArgs(command::FieldId field, algo::strptr& out_dflt, bool* out_anon) {
29156    i32 retval = 1;
29157    switch (field) {
29158        case command_FieldId_in: { // $comment
29159            *out_anon = false;
29160        } break;
29161        case command_FieldId_begin: { // $comment
29162            *out_anon = false;
29163            retval=0;
29164            out_dflt="Y";
29165        } break;
29166        case command_FieldId_end: { // bool: no argument required but value may be specified as begin:Y
29167            *out_anon = false;
29168            retval=0;
29169            out_dflt="Y";
29170        } break;
29171        case command_FieldId_stay: { // bool: no argument required but value may be specified as end:Y
29172            *out_anon = false;
29173            retval=0;
29174            out_dflt="Y";
29175        } break;
29176        case command_FieldId_logfile: { // bool: no argument required but value may be specified as stay:Y
29177            *out_anon = false;
29178        } break;
29179        case command_FieldId_report: { // bool: no argument required but value may be specified as stay:Y
29180            *out_anon = false;
29181            retval=0;
29182            out_dflt="Y";
29183        } break;
29184        case command_FieldId_from: { // bool: no argument required but value may be specified as report:Y
29185            *out_anon = false;
29186        } break;
29187        case command_FieldId_to: { // bool: no argument required but value may be specified as report:Y
29188            *out_anon = false;
29189        } break;
29190        case command_FieldId_e: { // bool: no argument required but value may be specified as report:Y
29191            *out_anon = false;
29192            retval=0;
29193            out_dflt="Y";
29194        } break;
29195        case command_FieldId_comment: { // bool: no argument required but value may be specified as e:Y
29196            *out_anon = false;
29197        } break;
29198        default:
29199        retval=-1; // unrecognized
29200    }
29201    return retval;
29202}
29203
29204// --- command.ttrack_proc.ttrack.Start
29205// Start subprocess
29206// If subprocess already running, do nothing. Otherwise, start it
29207int command::ttrack_Start(command::ttrack_proc& parent) {
29208    int retval = 0;
29209    if (parent.pid == 0) {
29210        verblog(ttrack_ToCmdline(parent)); // maybe print command
29211#ifdef WIN32
29212        algo_lib::ResolveExecFname(parent.path);
29213        tempstr cmdline(ttrack_ToCmdline(parent));
29214        parent.pid = dospawn(Zeroterm(parent.path),Zeroterm(cmdline),parent.timeout,parent.fstdin,parent.fstdout,parent.fstderr);
29215#else
29216        parent.pid = fork();
29217        if (parent.pid == 0) { // child
29218            algo_lib::DieWithParent();
29219            if (parent.timeout > 0) {
29220                alarm(parent.timeout);
29221            }
29222            if (retval==0) retval=algo_lib::ApplyRedirect(parent.fstdin , 0);
29223            if (retval==0) retval=algo_lib::ApplyRedirect(parent.fstdout, 1);
29224            if (retval==0) retval=algo_lib::ApplyRedirect(parent.fstderr, 2);
29225            if (retval==0) retval= ttrack_Execv(parent);
29226            if (retval != 0) { // if start fails, print error
29227                int err=errno;
29228                prerr("command.ttrack_execv"
29229                <<Keyval("errno",err)
29230                <<Keyval("errstr",strerror(err))
29231                <<Keyval("comment","Execv failed"));
29232            }
29233            _exit(127); // if failed to start, exit anyway
29234        } else if (parent.pid == -1) {
29235            retval = errno; // failed to fork
29236        }
29237#endif
29238    }
29239    parent.status = parent.pid > 0 ? 0 : -1; // if didn't start, set error status
29240    return retval;
29241}
29242
29243// --- command.ttrack_proc.ttrack.StartRead
29244// Start subprocess & Read output
29245algo::Fildes command::ttrack_StartRead(command::ttrack_proc& parent, algo_lib::FFildes &read) {
29246    int pipefd[2];
29247    int rc=pipe(pipefd);
29248    (void)rc;
29249    read.fd.value = pipefd[0];
29250    parent.fstdout  << ">&" << pipefd[1];
29251    ttrack_Start(parent);
29252    (void)close(pipefd[1]);
29253    return read.fd;
29254}
29255
29256// --- command.ttrack_proc.ttrack.Kill
29257// Kill subprocess and wait
29258void command::ttrack_Kill(command::ttrack_proc& parent) {
29259    if (parent.pid != 0) {
29260        kill(parent.pid,9);
29261        ttrack_Wait(parent);
29262    }
29263}
29264
29265// --- command.ttrack_proc.ttrack.Wait
29266// Wait for subprocess to return
29267void command::ttrack_Wait(command::ttrack_proc& parent) {
29268    if (parent.pid > 0) {
29269        int wait_flags = 0;
29270        int wait_status = 0;
29271        int rc = -1;
29272        do {
29273            // really wait for subprocess to exit
29274            rc = waitpid(parent.pid,&wait_status,wait_flags);
29275        } while (rc==-1 && errno==EINTR);
29276        if (rc == parent.pid) {
29277            parent.status = wait_status;
29278            parent.pid = 0;
29279        }
29280    }
29281}
29282
29283// --- command.ttrack_proc.ttrack.Exec
29284// Start + Wait
29285// Execute subprocess and return exit code
29286int command::ttrack_Exec(command::ttrack_proc& parent) {
29287    ttrack_Start(parent);
29288    ttrack_Wait(parent);
29289    return parent.status;
29290}
29291
29292// --- command.ttrack_proc.ttrack.ExecX
29293// Start + Wait, throw exception on error
29294// Execute subprocess; throw human-readable exception on error
29295void command::ttrack_ExecX(command::ttrack_proc& parent) {
29296    int rc = ttrack_Exec(parent);
29297    vrfy(rc==0, tempstr() << "algo_lib.exec" << Keyval("cmd",ttrack_ToCmdline(parent))
29298    << Keyval("comment",algo::DescribeWaitStatus(parent.status)));
29299}
29300
29301// --- command.ttrack_proc.ttrack.Execv
29302// Call execv()
29303// Call execv with specified parameters
29304int command::ttrack_Execv(command::ttrack_proc& parent) {
29305    int ret = 0;
29306    algo::StringAry args;
29307    ttrack_ToArgv(parent, args);
29308    char **argv = (char**)alloca((ary_N(args)+1)*sizeof(*argv));
29309    ind_beg(algo::StringAry_ary_curs,arg,args) {
29310        argv[ind_curs(arg).index] = Zeroterm(arg);
29311    }ind_end;
29312    argv[ary_N(args)] = NULL;
29313    // if parent.path is relative, search for it in PATH
29314    algo_lib::ResolveExecFname(parent.path);
29315    ret = execv(Zeroterm(parent.path),argv);
29316    return ret;
29317}
29318
29319// --- command.ttrack_proc.ttrack.ToCmdline
29320algo::tempstr command::ttrack_ToCmdline(command::ttrack_proc& parent) {
29321    algo::tempstr retval;
29322    retval << parent.path << " ";
29323    command::ttrack_PrintArgv(parent.cmd,retval);
29324    if (ch_N(parent.fstdin)) {
29325        retval << " " << parent.fstdin;
29326    }
29327    if (ch_N(parent.fstdout)) {
29328        retval << " " << parent.fstdout;
29329    }
29330    if (ch_N(parent.fstderr)) {
29331        retval << " 2" << parent.fstderr;
29332    }
29333    return retval;
29334}
29335
29336// --- command.ttrack_proc.ttrack.ToArgv
29337// Form array from the command line
29338void command::ttrack_ToArgv(command::ttrack_proc& parent, algo::StringAry& args) {
29339    ary_RemoveAll(args);
29340    ary_Alloc(args) << parent.path;
29341
29342    if (parent.cmd.in != "data") {
29343        cstring *arg = &ary_Alloc(args);
29344        *arg << "-in:";
29345        cstring_Print(parent.cmd.in, *arg);
29346    }
29347
29348    if (parent.cmd.begin != false) {
29349        cstring *arg = &ary_Alloc(args);
29350        *arg << "-begin:";
29351        bool_Print(parent.cmd.begin, *arg);
29352    }
29353
29354    if (parent.cmd.end != false) {
29355        cstring *arg = &ary_Alloc(args);
29356        *arg << "-end:";
29357        bool_Print(parent.cmd.end, *arg);
29358    }
29359
29360    if (parent.cmd.stay != false) {
29361        cstring *arg = &ary_Alloc(args);
29362        *arg << "-stay:";
29363        bool_Print(parent.cmd.stay, *arg);
29364    }
29365
29366    if (parent.cmd.logfile != "~/.ssim/ttrack.ssim") {
29367        cstring *arg = &ary_Alloc(args);
29368        *arg << "-logfile:";
29369        cstring_Print(parent.cmd.logfile, *arg);
29370    }
29371
29372    if (parent.cmd.report != true) {
29373        cstring *arg = &ary_Alloc(args);
29374        *arg << "-report:";
29375        bool_Print(parent.cmd.report, *arg);
29376    }
29377
29378    if (true) {
29379        cstring *arg = &ary_Alloc(args);
29380        *arg << "-from:";
29381        UnTime_Print(parent.cmd.from, *arg);
29382    }
29383
29384    if (true) {
29385        cstring *arg = &ary_Alloc(args);
29386        *arg << "-to:";
29387        UnTime_Print(parent.cmd.to, *arg);
29388    }
29389
29390    if (parent.cmd.e != false) {
29391        cstring *arg = &ary_Alloc(args);
29392        *arg << "-e:";
29393        bool_Print(parent.cmd.e, *arg);
29394    }
29395
29396    if (parent.cmd.comment != "") {
29397        cstring *arg = &ary_Alloc(args);
29398        *arg << "-comment:";
29399        cstring_Print(parent.cmd.comment, *arg);
29400    }
29401    for (int i=1; i < algo_lib::_db.cmdline.verbose; ++i) {
29402        ary_Alloc(args) << "-verbose";
29403    }
29404}
29405
29406// --- command.ttrack_proc..Uninit
29407void command::ttrack_proc_Uninit(command::ttrack_proc& parent) {
29408    command::ttrack_proc &row = parent; (void)row;
29409
29410    // command.ttrack_proc.ttrack.Uninit (Exec)  //
29411    ttrack_Kill(parent); // kill child, ensure forward progress
29412}
29413
29414// --- command.wcli.fields.Addary
29415// Reserve space (this may move memory). Insert N element at the end.
29416// Return aryptr to newly inserted block.
29417// If the RHS argument aliases the array (refers to the same memory), exit program with fatal error.
29418algo::aryptr<algo::cstring> command::fields_Addary(command::wcli& parent, algo::aryptr<algo::cstring> rhs) {
29419    bool overlaps = rhs.n_elems>0 && rhs.elems >= parent.fields_elems && rhs.elems < parent.fields_elems + parent.fields_max;
29420    if (UNLIKELY(overlaps)) {
29421        FatalErrorExit("command.tary_alias  field:command.wcli.fields  comment:'alias error: sub-array is being appended to the whole'");
29422    }
29423    int nnew = rhs.n_elems;
29424    fields_Reserve(parent, nnew); // reserve space
29425    int at = parent.fields_n;
29426    for (int i = 0; i < nnew; i++) {
29427        new (parent.fields_elems + at + i) algo::cstring(rhs[i]);
29428        parent.fields_n++;
29429    }
29430    return algo::aryptr<algo::cstring>(parent.fields_elems + at, nnew);
29431}
29432
29433// --- command.wcli.fields.Alloc
29434// Reserve space. Insert element at the end
29435// The new element is initialized to a default value
29436algo::cstring& command::fields_Alloc(command::wcli& parent) {
29437    fields_Reserve(parent, 1);
29438    int n  = parent.fields_n;
29439    int at = n;
29440    algo::cstring *elems = parent.fields_elems;
29441    new (elems + at) algo::cstring(""); // construct new element, default initializer
29442    parent.fields_n = n+1;
29443    return elems[at];
29444}
29445
29446// --- command.wcli.fields.AllocAt
29447// Reserve space for new element, reallocating the array if necessary
29448// Insert new element at specified index. Index must be in range or a fatal error occurs.
29449algo::cstring& command::fields_AllocAt(command::wcli& parent, int at) {
29450    fields_Reserve(parent, 1);
29451    int n  = parent.fields_n;
29452    if (UNLIKELY(u64(at) >= u64(n+1))) {
29453        FatalErrorExit("command.bad_alloc_at  field:command.wcli.fields  comment:'index out of range'");
29454    }
29455    algo::cstring *elems = parent.fields_elems;
29456    memmove(elems + at + 1, elems + at, (n - at) * sizeof(algo::cstring));
29457    new (elems + at) algo::cstring(""); // construct element, default initializer
29458    parent.fields_n = n+1;
29459    return elems[at];
29460}
29461
29462// --- command.wcli.fields.AllocN
29463// Reserve space. Insert N elements at the end of the array, return pointer to array
29464algo::aryptr<algo::cstring> command::fields_AllocN(command::wcli& parent, int n_elems) {
29465    fields_Reserve(parent, n_elems);
29466    int old_n  = parent.fields_n;
29467    int new_n = old_n + n_elems;
29468    algo::cstring *elems = parent.fields_elems;
29469    for (int i = old_n; i < new_n; i++) {
29470        new (elems + i) algo::cstring(""); // construct new element, default initialize
29471    }
29472    parent.fields_n = new_n;
29473    return algo::aryptr<algo::cstring>(elems + old_n, n_elems);
29474}
29475
29476// --- command.wcli.fields.Remove
29477// Remove item by index. If index outside of range, do nothing.
29478void command::fields_Remove(command::wcli& parent, u32 i) {
29479    u32 lim = parent.fields_n;
29480    algo::cstring *elems = parent.fields_elems;
29481    if (i < lim) {
29482        elems[i].~cstring(); // destroy element
29483        memmove(elems + i, elems + (i + 1), sizeof(algo::cstring) * (lim - (i + 1)));
29484        parent.fields_n = lim - 1;
29485    }
29486}
29487
29488// --- command.wcli.fields.RemoveAll
29489void command::fields_RemoveAll(command::wcli& parent) {
29490    u32 n = parent.fields_n;
29491    while (n > 0) {
29492        n -= 1;
29493        parent.fields_elems[n].~cstring();
29494        parent.fields_n = n;
29495    }
29496}
29497
29498// --- command.wcli.fields.RemoveLast
29499// Delete last element of array. Do nothing if array is empty.
29500void command::fields_RemoveLast(command::wcli& parent) {
29501    u64 n = parent.fields_n;
29502    if (n > 0) {
29503        n -= 1;
29504        fields_qFind(parent, u64(n)).~cstring();
29505        parent.fields_n = n;
29506    }
29507}
29508
29509// --- command.wcli.fields.AbsReserve
29510// Make sure N elements fit in array. Process dies if out of memory
29511void command::fields_AbsReserve(command::wcli& parent, int n) {
29512    u32 old_max  = parent.fields_max;
29513    if (n > i32(old_max)) {
29514        u32 new_max  = i32_Max(i32_Max(old_max * 2, n), 4);
29515        void *new_mem = algo_lib::malloc_ReallocMem(parent.fields_elems, old_max * sizeof(algo::cstring), new_max * sizeof(algo::cstring));
29516        if (UNLIKELY(!new_mem)) {
29517            FatalErrorExit("command.tary_nomem  field:command.wcli.fields  comment:'out of memory'");
29518        }
29519        parent.fields_elems = (algo::cstring*)new_mem;
29520        parent.fields_max = new_max;
29521    }
29522}
29523
29524// --- command.wcli.fields.Setary
29525// Copy contents of RHS to PARENT.
29526void command::fields_Setary(command::wcli& parent, command::wcli &rhs) {
29527    fields_RemoveAll(parent);
29528    int nnew = rhs.fields_n;
29529    fields_Reserve(parent, nnew); // reserve space
29530    for (int i = 0; i < nnew; i++) { // copy elements over
29531        new (parent.fields_elems + i) algo::cstring(fields_qFind(rhs, i));
29532        parent.fields_n = i + 1;
29533    }
29534}
29535
29536// --- command.wcli.fields.Setary2
29537// Copy specified array into fields, discarding previous contents.
29538// If the RHS argument aliases the array (refers to the same memory), throw exception.
29539void command::fields_Setary(command::wcli& parent, const algo::aryptr<algo::cstring> &rhs) {
29540    fields_RemoveAll(parent);
29541    fields_Addary(parent, rhs);
29542}
29543
29544// --- command.wcli.fields.AllocNVal
29545// Reserve space. Insert N elements at the end of the array, return pointer to array
29546algo::aryptr<algo::cstring> command::fields_AllocNVal(command::wcli& parent, int n_elems, const algo::cstring& val) {
29547    fields_Reserve(parent, n_elems);
29548    int old_n  = parent.fields_n;
29549    int new_n = old_n + n_elems;
29550    algo::cstring *elems = parent.fields_elems;
29551    for (int i = old_n; i < new_n; i++) {
29552        new (elems + i) algo::cstring(val);
29553    }
29554    parent.fields_n = new_n;
29555    return algo::aryptr<algo::cstring>(elems + old_n, n_elems);
29556}
29557
29558// --- command.wcli.fields.ReadStrptrMaybe
29559// A single element is read from input string and appended to the array.
29560// If the string contains an error, the array is untouched.
29561// Function returns success value.
29562bool command::fields_ReadStrptrMaybe(command::wcli& parent, algo::strptr in_str) {
29563    bool retval = true;
29564    algo::cstring &elem = fields_Alloc(parent);
29565    retval = algo::cstring_ReadStrptrMaybe(elem, in_str);
29566    if (!retval) {
29567        fields_RemoveLast(parent);
29568    }
29569    return retval;
29570}
29571
29572// --- command.wcli..ReadFieldMaybe
29573bool command::wcli_ReadFieldMaybe(command::wcli& parent, algo::strptr field, algo::strptr strval) {
29574    bool retval = true;
29575    command::FieldId field_id;
29576    (void)value_SetStrptrMaybe(field_id,algo::Pathcomp(field, ".LL"));
29577    switch(field_id) {
29578        case command_FieldId_selector: {
29579            retval = algo::Smallstr50_ReadStrptrMaybe(parent.selector, strval);
29580            break;
29581        }
29582        case command_FieldId_fields: {
29583            retval = fields_ReadStrptrMaybe(parent, strval);
29584            break;
29585        }
29586        case command_FieldId_list: {
29587            retval = bool_ReadStrptrMaybe(parent.list, strval);
29588            break;
29589        }
29590        case command_FieldId_create: {
29591            retval = bool_ReadStrptrMaybe(parent.create, strval);
29592            break;
29593        }
29594        case command_FieldId_update: {
29595            retval = bool_ReadStrptrMaybe(parent.update, strval);
29596            break;
29597        }
29598        case command_FieldId_download: {
29599            retval = bool_ReadStrptrMaybe(parent.download, strval);
29600            break;
29601        }
29602        case command_FieldId_upload: {
29603            retval = bool_ReadStrptrMaybe(parent.upload, strval);
29604            break;
29605        }
29606        case command_FieldId_remove: {
29607            retval = bool_ReadStrptrMaybe(parent.remove, strval);
29608            break;
29609        }
29610        case command_FieldId_t: {
29611            retval = bool_ReadStrptrMaybe(parent.t, strval);
29612            break;
29613        }
29614        case command_FieldId_e: {
29615            retval = bool_ReadStrptrMaybe(parent.e, strval);
29616            break;
29617        }
29618        case command_FieldId_ignore_saved_edit: {
29619            retval = bool_ReadStrptrMaybe(parent.ignore_saved_edit, strval);
29620            break;
29621        }
29622        case command_FieldId_exclude_subpages: {
29623            retval = bool_ReadStrptrMaybe(parent.exclude_subpages, strval);
29624            break;
29625        }
29626        case command_FieldId_authdir: {
29627            retval = algo::cstring_ReadStrptrMaybe(parent.authdir, strval);
29628            break;
29629        }
29630        case command_FieldId_pagedir: {
29631            retval = algo::cstring_ReadStrptrMaybe(parent.pagedir, strval);
29632            break;
29633        }
29634        case command_FieldId_in: {
29635            retval = algo::cstring_ReadStrptrMaybe(parent.in, strval);
29636            break;
29637        }
29638        case command_FieldId_dry_run: {
29639            retval = bool_ReadStrptrMaybe(parent.dry_run, strval);
29640            break;
29641        }
29642        default: break;
29643    }
29644    if (!retval) {
29645        algo_lib::AppendErrtext("attr",field);
29646    }
29647    return retval;
29648}
29649
29650// --- command.wcli..ReadTupleMaybe
29651// Read fields of command::wcli from attributes of ascii tuple TUPLE
29652bool command::wcli_ReadTupleMaybe(command::wcli &parent, algo::Tuple &tuple) {
29653    bool retval = true;
29654    int anon_idx = 0;
29655    ind_beg(algo::Tuple_attrs_curs,attr,tuple) {
29656        if (ch_N(attr.name) == 0) {
29657            attr.name = wcli_GetAnon(parent, anon_idx++);
29658        }
29659        retval = wcli_ReadFieldMaybe(parent, attr.name, attr.value);
29660        if (!retval) {
29661            break;
29662        }
29663    }ind_end;
29664    return retval;
29665}
29666
29667// --- command.wcli..Init
29668// Set all fields to initial values.
29669void command::wcli_Init(command::wcli& parent) {
29670    parent.selector = algo::strptr("issue:%");
29671    parent.fields_elems 	= 0; // (command.wcli.fields)
29672    parent.fields_n     	= 0; // (command.wcli.fields)
29673    parent.fields_max   	= 0; // (command.wcli.fields)
29674    parent.list = bool(false);
29675    parent.create = bool(false);
29676    parent.update = bool(false);
29677    parent.download = bool(false);
29678    parent.upload = bool(false);
29679    parent.remove = bool(false);
29680    parent.t = bool(false);
29681    parent.e = bool(false);
29682    parent.ignore_saved_edit = bool(false);
29683    parent.exclude_subpages = bool(false);
29684    parent.authdir = algo::strptr(".ssim");
29685    parent.pagedir = algo::strptr("temp/wcli/download");
29686    parent.in = algo::strptr("data");
29687    parent.dry_run = bool(false);
29688}
29689
29690// --- command.wcli..Uninit
29691void command::wcli_Uninit(command::wcli& parent) {
29692    command::wcli &row = parent; (void)row;
29693
29694    // command.wcli.fields.Uninit (Tary)  //additional key:value pairs for use with -create, -list, -update
29695    // remove all elements from command.wcli.fields
29696    fields_RemoveAll(parent);
29697    // free memory for Tary command.wcli.fields
29698    algo_lib::malloc_FreeMem(parent.fields_elems, sizeof(algo::cstring)*parent.fields_max); // (command.wcli.fields)
29699}
29700
29701// --- command.wcli..ToCmdline
29702// Convenience function that returns a full command line
29703// Assume command is in a directory called bin
29704tempstr command::wcli_ToCmdline(command::wcli& row) {
29705    tempstr ret;
29706    ret << "bin/wcli ";
29707    wcli_PrintArgv(row, ret);
29708    // inherit less intense verbose, debug options
29709    for (int i = 1; i < algo_lib::_db.cmdline.verbose; i++) {
29710        ret << " -verbose";
29711    }
29712    for (int i = 1; i < algo_lib::_db.cmdline.debug; i++) {
29713        ret << " -debug";
29714    }
29715    return ret;
29716}
29717
29718// --- command.wcli..PrintArgv
29719// print string representation of ROW to string STR
29720// cfmt:command.wcli.Argv  printfmt:Tuple
29721void command::wcli_PrintArgv(command::wcli& row, algo::cstring& str) {
29722    algo::tempstr temp;
29723    (void)temp;
29724    (void)str;
29725    ch_RemoveAll(temp);
29726    Smallstr50_Print(row.selector, temp);
29727    str << " -selector:";
29728    strptr_PrintBash(temp,str);
29729    ind_beg(wcli_fields_curs,value,row) {
29730        ch_RemoveAll(temp);
29731        cstring_Print(value, temp);
29732        str << " -fields:";
29733        strptr_PrintBash(temp,str);
29734    }ind_end;
29735    if (!(row.list == false)) {
29736        ch_RemoveAll(temp);
29737        bool_Print(row.list, temp);
29738        str << " -list:";
29739        strptr_PrintBash(temp,str);
29740    }
29741    if (!(row.create == false)) {
29742        ch_RemoveAll(temp);
29743        bool_Print(row.create, temp);
29744        str << " -create:";
29745        strptr_PrintBash(temp,str);
29746    }
29747    if (!(row.update == false)) {
29748        ch_RemoveAll(temp);
29749        bool_Print(row.update, temp);
29750        str << " -update:";
29751        strptr_PrintBash(temp,str);
29752    }
29753    if (!(row.download == false)) {
29754        ch_RemoveAll(temp);
29755        bool_Print(row.download, temp);
29756        str << " -download:";
29757        strptr_PrintBash(temp,str);
29758    }
29759    if (!(row.upload == false)) {
29760        ch_RemoveAll(temp);
29761        bool_Print(row.upload, temp);
29762        str << " -upload:";
29763        strptr_PrintBash(temp,str);
29764    }
29765    if (!(row.remove == false)) {
29766        ch_RemoveAll(temp);
29767        bool_Print(row.remove, temp);
29768        str << " -remove:";
29769        strptr_PrintBash(temp,str);
29770    }
29771    if (!(row.t == false)) {
29772        ch_RemoveAll(temp);
29773        bool_Print(row.t, temp);
29774        str << " -t:";
29775        strptr_PrintBash(temp,str);
29776    }
29777    if (!(row.e == false)) {
29778        ch_RemoveAll(temp);
29779        bool_Print(row.e, temp);
29780        str << " -e:";
29781        strptr_PrintBash(temp,str);
29782    }
29783    if (!(row.ignore_saved_edit == false)) {
29784        ch_RemoveAll(temp);
29785        bool_Print(row.ignore_saved_edit, temp);
29786        str << " -ignore_saved_edit:";
29787        strptr_PrintBash(temp,str);
29788    }
29789    if (!(row.exclude_subpages == false)) {
29790        ch_RemoveAll(temp);
29791        bool_Print(row.exclude_subpages, temp);
29792        str << " -exclude_subpages:";
29793        strptr_PrintBash(temp,str);
29794    }
29795    if (!(row.authdir == ".ssim")) {
29796        ch_RemoveAll(temp);
29797        cstring_Print(row.authdir, temp);
29798        str << " -authdir:";
29799        strptr_PrintBash(temp,str);
29800    }
29801    if (!(row.pagedir == "temp/wcli/download")) {
29802        ch_RemoveAll(temp);
29803        cstring_Print(row.pagedir, temp);
29804        str << " -pagedir:";
29805        strptr_PrintBash(temp,str);
29806    }
29807    if (!(row.in == "data")) {
29808        ch_RemoveAll(temp);
29809        cstring_Print(row.in, temp);
29810        str << " -in:";
29811        strptr_PrintBash(temp,str);
29812    }
29813    if (!(row.dry_run == false)) {
29814        ch_RemoveAll(temp);
29815        bool_Print(row.dry_run, temp);
29816        str << " -dry_run:";
29817        strptr_PrintBash(temp,str);
29818    }
29819}
29820
29821// --- command.wcli..GetAnon
29822algo::strptr command::wcli_GetAnon(command::wcli &parent, i32 idx) {
29823    (void)parent;//only to avoid -Wunused-parameter
29824    switch(idx) {
29825        case(0): return strptr("selector", 8);
29826        default: return strptr("fields", 6);
29827    }
29828}
29829
29830// --- command.wcli..NArgs
29831// Used with command lines
29832// Return # of command-line arguments that must follow this argument
29833// If FIELD is invalid, return -1
29834i32 command::wcli_NArgs(command::FieldId field, algo::strptr& out_dflt, bool* out_anon) {
29835    i32 retval = 1;
29836    switch (field) {
29837        case command_FieldId_selector: { // $comment
29838            *out_anon = true;
29839        } break;
29840        case command_FieldId_fields: { // $comment
29841            *out_anon = true;
29842        } break;
29843        case command_FieldId_list: { // $comment
29844            *out_anon = false;
29845            retval=0;
29846            out_dflt="Y";
29847        } break;
29848        case command_FieldId_create: { // bool: no argument required but value may be specified as list:Y
29849            *out_anon = false;
29850            retval=0;
29851            out_dflt="Y";
29852        } break;
29853        case command_FieldId_update: { // bool: no argument required but value may be specified as create:Y
29854            *out_anon = false;
29855            retval=0;
29856            out_dflt="Y";
29857        } break;
29858        case command_FieldId_download: { // bool: no argument required but value may be specified as update:Y
29859            *out_anon = false;
29860            retval=0;
29861            out_dflt="Y";
29862        } break;
29863        case command_FieldId_upload: { // bool: no argument required but value may be specified as download:Y
29864            *out_anon = false;
29865            retval=0;
29866            out_dflt="Y";
29867        } break;
29868        case command_FieldId_remove: { // bool: no argument required but value may be specified as upload:Y
29869            *out_anon = false;
29870            retval=0;
29871            out_dflt="Y";
29872        } break;
29873        case command_FieldId_t: { // bool: no argument required but value may be specified as remove:Y
29874            *out_anon = false;
29875            retval=0;
29876            out_dflt="Y";
29877        } break;
29878        case command_FieldId_e: { // bool: no argument required but value may be specified as t:Y
29879            *out_anon = false;
29880            retval=0;
29881            out_dflt="Y";
29882        } break;
29883        case command_FieldId_ignore_saved_edit: { // bool: no argument required but value may be specified as e:Y
29884            *out_anon = false;
29885            retval=0;
29886            out_dflt="Y";
29887        } break;
29888        case command_FieldId_exclude_subpages: { // bool: no argument required but value may be specified as ignore_saved_edit:Y
29889            *out_anon = false;
29890            retval=0;
29891            out_dflt="Y";
29892        } break;
29893        case command_FieldId_authdir: { // bool: no argument required but value may be specified as exclude_subpages:Y
29894            *out_anon = false;
29895        } break;
29896        case command_FieldId_pagedir: { // bool: no argument required but value may be specified as exclude_subpages:Y
29897            *out_anon = false;
29898        } break;
29899        case command_FieldId_in: { // bool: no argument required but value may be specified as exclude_subpages:Y
29900            *out_anon = false;
29901        } break;
29902        case command_FieldId_dry_run: { // bool: no argument required but value may be specified as exclude_subpages:Y
29903            *out_anon = false;
29904            retval=0;
29905            out_dflt="Y";
29906        } break;
29907        default:
29908        retval=-1; // unrecognized
29909    }
29910    return retval;
29911}
29912
29913// --- command.wcli_proc.wcli.Start
29914// Start subprocess
29915// If subprocess already running, do nothing. Otherwise, start it
29916int command::wcli_Start(command::wcli_proc& parent) {
29917    int retval = 0;
29918    if (parent.pid == 0) {
29919        verblog(wcli_ToCmdline(parent)); // maybe print command
29920#ifdef WIN32
29921        algo_lib::ResolveExecFname(parent.path);
29922        tempstr cmdline(wcli_ToCmdline(parent));
29923        parent.pid = dospawn(Zeroterm(parent.path),Zeroterm(cmdline),parent.timeout,parent.fstdin,parent.fstdout,parent.fstderr);
29924#else
29925        parent.pid = fork();
29926        if (parent.pid == 0) { // child
29927            algo_lib::DieWithParent();
29928            if (parent.timeout > 0) {
29929                alarm(parent.timeout);
29930            }
29931            if (retval==0) retval=algo_lib::ApplyRedirect(parent.fstdin , 0);
29932            if (retval==0) retval=algo_lib::ApplyRedirect(parent.fstdout, 1);
29933            if (retval==0) retval=algo_lib::ApplyRedirect(parent.fstderr, 2);
29934            if (retval==0) retval= wcli_Execv(parent);
29935            if (retval != 0) { // if start fails, print error
29936                int err=errno;
29937                prerr("command.wcli_execv"
29938                <<Keyval("errno",err)
29939                <<Keyval("errstr",strerror(err))
29940                <<Keyval("comment","Execv failed"));
29941            }
29942            _exit(127); // if failed to start, exit anyway
29943        } else if (parent.pid == -1) {
29944            retval = errno; // failed to fork
29945        }
29946#endif
29947    }
29948    parent.status = parent.pid > 0 ? 0 : -1; // if didn't start, set error status
29949    return retval;
29950}
29951
29952// --- command.wcli_proc.wcli.StartRead
29953// Start subprocess & Read output
29954algo::Fildes command::wcli_StartRead(command::wcli_proc& parent, algo_lib::FFildes &read) {
29955    int pipefd[2];
29956    int rc=pipe(pipefd);
29957    (void)rc;
29958    read.fd.value = pipefd[0];
29959    parent.fstdout  << ">&" << pipefd[1];
29960    wcli_Start(parent);
29961    (void)close(pipefd[1]);
29962    return read.fd;
29963}
29964
29965// --- command.wcli_proc.wcli.Kill
29966// Kill subprocess and wait
29967void command::wcli_Kill(command::wcli_proc& parent) {
29968    if (parent.pid != 0) {
29969        kill(parent.pid,9);
29970        wcli_Wait(parent);
29971    }
29972}
29973
29974// --- command.wcli_proc.wcli.Wait
29975// Wait for subprocess to return
29976void command::wcli_Wait(command::wcli_proc& parent) {
29977    if (parent.pid > 0) {
29978        int wait_flags = 0;
29979        int wait_status = 0;
29980        int rc = -1;
29981        do {
29982            // really wait for subprocess to exit
29983            rc = waitpid(parent.pid,&wait_status,wait_flags);
29984        } while (rc==-1 && errno==EINTR);
29985        if (rc == parent.pid) {
29986            parent.status = wait_status;
29987            parent.pid = 0;
29988        }
29989    }
29990}
29991
29992// --- command.wcli_proc.wcli.Exec
29993// Start + Wait
29994// Execute subprocess and return exit code
29995int command::wcli_Exec(command::wcli_proc& parent) {
29996    wcli_Start(parent);
29997    wcli_Wait(parent);
29998    return parent.status;
29999}
30000
30001// --- command.wcli_proc.wcli.ExecX
30002// Start + Wait, throw exception on error
30003// Execute subprocess; throw human-readable exception on error
30004void command::wcli_ExecX(command::wcli_proc& parent) {
30005    int rc = wcli_Exec(parent);
30006    vrfy(rc==0, tempstr() << "algo_lib.exec" << Keyval("cmd",wcli_ToCmdline(parent))
30007    << Keyval("comment",algo::DescribeWaitStatus(parent.status)));
30008}
30009
30010// --- command.wcli_proc.wcli.Execv
30011// Call execv()
30012// Call execv with specified parameters
30013int command::wcli_Execv(command::wcli_proc& parent) {
30014    int ret = 0;
30015    algo::StringAry args;
30016    wcli_ToArgv(parent, args);
30017    char **argv = (char**)alloca((ary_N(args)+1)*sizeof(*argv));
30018    ind_beg(algo::StringAry_ary_curs,arg,args) {
30019        argv[ind_curs(arg).index] = Zeroterm(arg);
30020    }ind_end;
30021    argv[ary_N(args)] = NULL;
30022    // if parent.path is relative, search for it in PATH
30023    algo_lib::ResolveExecFname(parent.path);
30024    ret = execv(Zeroterm(parent.path),argv);
30025    return ret;
30026}
30027
30028// --- command.wcli_proc.wcli.ToCmdline
30029algo::tempstr command::wcli_ToCmdline(command::wcli_proc& parent) {
30030    algo::tempstr retval;
30031    retval << parent.path << " ";
30032    command::wcli_PrintArgv(parent.cmd,retval);
30033    if (ch_N(parent.fstdin)) {
30034        retval << " " << parent.fstdin;
30035    }
30036    if (ch_N(parent.fstdout)) {
30037        retval << " " << parent.fstdout;
30038    }
30039    if (ch_N(parent.fstderr)) {
30040        retval << " 2" << parent.fstderr;
30041    }
30042    return retval;
30043}
30044
30045// --- command.wcli_proc.wcli.ToArgv
30046// Form array from the command line
30047void command::wcli_ToArgv(command::wcli_proc& parent, algo::StringAry& args) {
30048    ary_RemoveAll(args);
30049    ary_Alloc(args) << parent.path;
30050
30051    if (parent.cmd.selector != "issue:%") {
30052        cstring *arg = &ary_Alloc(args);
30053        *arg << "-selector:";
30054        Smallstr50_Print(parent.cmd.selector, *arg);
30055    }
30056    ind_beg(command::wcli_fields_curs,value,parent.cmd) {
30057        cstring *arg = &ary_Alloc(args);
30058        *arg << "-fields:";
30059        cstring_Print(value, *arg);
30060    }ind_end;
30061
30062    if (parent.cmd.list != false) {
30063        cstring *arg = &ary_Alloc(args);
30064        *arg << "-list:";
30065        bool_Print(parent.cmd.list, *arg);
30066    }
30067
30068    if (parent.cmd.create != false) {
30069        cstring *arg = &ary_Alloc(args);
30070        *arg << "-create:";
30071        bool_Print(parent.cmd.create, *arg);
30072    }
30073
30074    if (parent.cmd.update != false) {
30075        cstring *arg = &ary_Alloc(args);
30076        *arg << "-update:";
30077        bool_Print(parent.cmd.update, *arg);
30078    }
30079
30080    if (parent.cmd.download != false) {
30081        cstring *arg = &ary_Alloc(args);
30082        *arg << "-download:";
30083        bool_Print(parent.cmd.download, *arg);
30084    }
30085
30086    if (parent.cmd.upload != false) {
30087        cstring *arg = &ary_Alloc(args);
30088        *arg << "-upload:";
30089        bool_Print(parent.cmd.upload, *arg);
30090    }
30091
30092    if (parent.cmd.remove != false) {
30093        cstring *arg = &ary_Alloc(args);
30094        *arg << "-remove:";
30095        bool_Print(parent.cmd.remove, *arg);
30096    }
30097
30098    if (parent.cmd.t != false) {
30099        cstring *arg = &ary_Alloc(args);
30100        *arg << "-t:";
30101        bool_Print(parent.cmd.t, *arg);
30102    }
30103
30104    if (parent.cmd.e != false) {
30105        cstring *arg = &ary_Alloc(args);
30106        *arg << "-e:";
30107        bool_Print(parent.cmd.e, *arg);
30108    }
30109
30110    if (parent.cmd.ignore_saved_edit != false) {
30111        cstring *arg = &ary_Alloc(args);
30112        *arg << "-ignore_saved_edit:";
30113        bool_Print(parent.cmd.ignore_saved_edit, *arg);
30114    }
30115
30116    if (parent.cmd.exclude_subpages != false) {
30117        cstring *arg = &ary_Alloc(args);
30118        *arg << "-exclude_subpages:";
30119        bool_Print(parent.cmd.exclude_subpages, *arg);
30120    }
30121
30122    if (parent.cmd.authdir != ".ssim") {
30123        cstring *arg = &ary_Alloc(args);
30124        *arg << "-authdir:";
30125        cstring_Print(parent.cmd.authdir, *arg);
30126    }
30127
30128    if (parent.cmd.pagedir != "temp/wcli/download") {
30129        cstring *arg = &ary_Alloc(args);
30130        *arg << "-pagedir:";
30131        cstring_Print(parent.cmd.pagedir, *arg);
30132    }
30133
30134    if (parent.cmd.in != "data") {
30135        cstring *arg = &ary_Alloc(args);
30136        *arg << "-in:";
30137        cstring_Print(parent.cmd.in, *arg);
30138    }
30139
30140    if (parent.cmd.dry_run != false) {
30141        cstring *arg = &ary_Alloc(args);
30142        *arg << "-dry_run:";
30143        bool_Print(parent.cmd.dry_run, *arg);
30144    }
30145    for (int i=1; i < algo_lib::_db.cmdline.verbose; ++i) {
30146        ary_Alloc(args) << "-verbose";
30147    }
30148}
30149
30150// --- command.wcli_proc..Uninit
30151void command::wcli_proc_Uninit(command::wcli_proc& parent) {
30152    command::wcli_proc &row = parent; (void)row;
30153
30154    // command.wcli_proc.wcli.Uninit (Exec)  //
30155    wcli_Kill(parent); // kill child, ensure forward progress
30156}
30157
30158// --- command.x2admin.envnode.Print
30159// Print back to string
30160void command::envnode_Print(command::x2admin& parent, algo::cstring &out) {
30161    Regx_Print(parent.envnode, out);
30162}
30163
30164// --- command.x2admin.envnode.ReadStrptrMaybe
30165// Read Regx from string
30166// Convert string to field. Return success value
30167bool command::envnode_ReadStrptrMaybe(command::x2admin& parent, algo::strptr in) {
30168    bool retval = true;
30169    Regx_ReadSql(parent.envnode, in, true);
30170    return retval;
30171}
30172
30173// --- command.x2admin.admsvc.Print
30174// Print back to string
30175void command::admsvc_Print(command::x2admin& parent, algo::cstring &out) {
30176    Regx_Print(parent.admsvc, out);
30177}
30178
30179// --- command.x2admin.admsvc.ReadStrptrMaybe
30180// Read Regx from string
30181// Convert string to field. Return success value
30182bool command::admsvc_ReadStrptrMaybe(command::x2admin& parent, algo::strptr in) {
30183    bool retval = true;
30184    Regx_ReadSql(parent.admsvc, in, true);
30185    return retval;
30186}
30187
30188// --- command.x2admin..ReadFieldMaybe
30189bool command::x2admin_ReadFieldMaybe(command::x2admin& parent, algo::strptr field, algo::strptr strval) {
30190    bool retval = true;
30191    command::FieldId field_id;
30192    (void)value_SetStrptrMaybe(field_id,field);
30193    switch(field_id) {
30194        case command_FieldId_in: {
30195            retval = algo::cstring_ReadStrptrMaybe(parent.in, strval);
30196            break;
30197        }
30198        case command_FieldId_envnode: {
30199            retval = envnode_ReadStrptrMaybe(parent, strval);
30200            break;
30201        }
30202        case command_FieldId_admsvc: {
30203            retval = admsvc_ReadStrptrMaybe(parent, strval);
30204            break;
30205        }
30206        case command_FieldId_generate: {
30207            retval = bool_ReadStrptrMaybe(parent.generate, strval);
30208            break;
30209        }
30210        case command_FieldId_update: {
30211            retval = bool_ReadStrptrMaybe(parent.update, strval);
30212            break;
30213        }
30214        case command_FieldId_verify: {
30215            retval = bool_ReadStrptrMaybe(parent.verify, strval);
30216            break;
30217        }
30218        case command_FieldId_show: {
30219            retval = bool_ReadStrptrMaybe(parent.show, strval);
30220            break;
30221        }
30222        case command_FieldId_dry_run: {
30223            retval = bool_ReadStrptrMaybe(parent.dry_run, strval);
30224            break;
30225        }
30226        default: break;
30227    }
30228    if (!retval) {
30229        algo_lib::AppendErrtext("attr",field);
30230    }
30231    return retval;
30232}
30233
30234// --- command.x2admin..ReadTupleMaybe
30235// Read fields of command::x2admin from attributes of ascii tuple TUPLE
30236bool command::x2admin_ReadTupleMaybe(command::x2admin &parent, algo::Tuple &tuple) {
30237    bool retval = true;
30238    ind_beg(algo::Tuple_attrs_curs,attr,tuple) {
30239        retval = x2admin_ReadFieldMaybe(parent, attr.name, attr.value);
30240        if (!retval) {
30241            break;
30242        }
30243    }ind_end;
30244    return retval;
30245}
30246
30247// --- command.x2admin..Init
30248// Set all fields to initial values.
30249void command::x2admin_Init(command::x2admin& parent) {
30250    parent.in = algo::strptr("data");
30251    Regx_ReadSql(parent.envnode, "%", true);
30252    Regx_ReadSql(parent.admsvc, "%", true);
30253    parent.generate = bool(false);
30254    parent.update = bool(false);
30255    parent.verify = bool(false);
30256    parent.show = bool(false);
30257    parent.dry_run = bool(false);
30258}
30259
30260// --- command.x2admin..ToCmdline
30261// Convenience function that returns a full command line
30262// Assume command is in a directory called bin
30263tempstr command::x2admin_ToCmdline(command::x2admin& row) {
30264    tempstr ret;
30265    ret << "bin/x2admin ";
30266    x2admin_PrintArgv(row, ret);
30267    // inherit less intense verbose, debug options
30268    for (int i = 1; i < algo_lib::_db.cmdline.verbose; i++) {
30269        ret << " -verbose";
30270    }
30271    for (int i = 1; i < algo_lib::_db.cmdline.debug; i++) {
30272        ret << " -debug";
30273    }
30274    return ret;
30275}
30276
30277// --- command.x2admin..PrintArgv
30278// print string representation of ROW to string STR
30279// cfmt:command.x2admin.Argv  printfmt:Tuple
30280void command::x2admin_PrintArgv(command::x2admin& row, algo::cstring& str) {
30281    algo::tempstr temp;
30282    (void)temp;
30283    (void)str;
30284    if (!(row.in == "data")) {
30285        ch_RemoveAll(temp);
30286        cstring_Print(row.in, temp);
30287        str << " -in:";
30288        strptr_PrintBash(temp,str);
30289    }
30290    if (!(row.envnode.expr == "%")) {
30291        ch_RemoveAll(temp);
30292        command::envnode_Print(const_cast<command::x2admin&>(row), temp);
30293        str << " -envnode:";
30294        strptr_PrintBash(temp,str);
30295    }
30296    if (!(row.admsvc.expr == "%")) {
30297        ch_RemoveAll(temp);
30298        command::admsvc_Print(const_cast<command::x2admin&>(row), temp);
30299        str << " -admsvc:";
30300        strptr_PrintBash(temp,str);
30301    }
30302    if (!(row.generate == false)) {
30303        ch_RemoveAll(temp);
30304        bool_Print(row.generate, temp);
30305        str << " -generate:";
30306        strptr_PrintBash(temp,str);
30307    }
30308    if (!(row.update == false)) {
30309        ch_RemoveAll(temp);
30310        bool_Print(row.update, temp);
30311        str << " -update:";
30312        strptr_PrintBash(temp,str);
30313    }
30314    if (!(row.verify == false)) {
30315        ch_RemoveAll(temp);
30316        bool_Print(row.verify, temp);
30317        str << " -verify:";
30318        strptr_PrintBash(temp,str);
30319    }
30320    if (!(row.show == false)) {
30321        ch_RemoveAll(temp);
30322        bool_Print(row.show, temp);
30323        str << " -show:";
30324        strptr_PrintBash(temp,str);
30325    }
30326    if (!(row.dry_run == false)) {
30327        ch_RemoveAll(temp);
30328        bool_Print(row.dry_run, temp);
30329        str << " -dry_run:";
30330        strptr_PrintBash(temp,str);
30331    }
30332}
30333
30334// --- command.x2admin..NArgs
30335// Used with command lines
30336// Return # of command-line arguments that must follow this argument
30337// If FIELD is invalid, return -1
30338i32 command::x2admin_NArgs(command::FieldId field, algo::strptr& out_dflt, bool* out_anon) {
30339    i32 retval = 1;
30340    switch (field) {
30341        case command_FieldId_in: { // $comment
30342            *out_anon = false;
30343        } break;
30344        case command_FieldId_envnode: { // $comment
30345            *out_anon = false;
30346        } break;
30347        case command_FieldId_admsvc: { // $comment
30348            *out_anon = false;
30349        } break;
30350        case command_FieldId_generate: { // $comment
30351            *out_anon = false;
30352            retval=0;
30353            out_dflt="Y";
30354        } break;
30355        case command_FieldId_update: { // bool: no argument required but value may be specified as generate:Y
30356            *out_anon = false;
30357            retval=0;
30358            out_dflt="Y";
30359        } break;
30360        case command_FieldId_verify: { // bool: no argument required but value may be specified as update:Y
30361            *out_anon = false;
30362            retval=0;
30363            out_dflt="Y";
30364        } break;
30365        case command_FieldId_show: { // bool: no argument required but value may be specified as verify:Y
30366            *out_anon = false;
30367            retval=0;
30368            out_dflt="Y";
30369        } break;
30370        case command_FieldId_dry_run: { // bool: no argument required but value may be specified as show:Y
30371            *out_anon = false;
30372            retval=0;
30373            out_dflt="Y";
30374        } break;
30375        default:
30376        retval=-1; // unrecognized
30377    }
30378    return retval;
30379}
30380
30381// --- command.x2admin_proc.x2admin.Start
30382// Start subprocess
30383// If subprocess already running, do nothing. Otherwise, start it
30384int command::x2admin_Start(command::x2admin_proc& parent) {
30385    int retval = 0;
30386    if (parent.pid == 0) {
30387        verblog(x2admin_ToCmdline(parent)); // maybe print command
30388#ifdef WIN32
30389        algo_lib::ResolveExecFname(parent.path);
30390        tempstr cmdline(x2admin_ToCmdline(parent));
30391        parent.pid = dospawn(Zeroterm(parent.path),Zeroterm(cmdline),parent.timeout,parent.fstdin,parent.fstdout,parent.fstderr);
30392#else
30393        parent.pid = fork();
30394        if (parent.pid == 0) { // child
30395            algo_lib::DieWithParent();
30396            if (parent.timeout > 0) {
30397                alarm(parent.timeout);
30398            }
30399            if (retval==0) retval=algo_lib::ApplyRedirect(parent.fstdin , 0);
30400            if (retval==0) retval=algo_lib::ApplyRedirect(parent.fstdout, 1);
30401            if (retval==0) retval=algo_lib::ApplyRedirect(parent.fstderr, 2);
30402            if (retval==0) retval= x2admin_Execv(parent);
30403            if (retval != 0) { // if start fails, print error
30404                int err=errno;
30405                prerr("command.x2admin_execv"
30406                <<Keyval("errno",err)
30407                <<Keyval("errstr",strerror(err))
30408                <<Keyval("comment","Execv failed"));
30409            }
30410            _exit(127); // if failed to start, exit anyway
30411        } else if (parent.pid == -1) {
30412            retval = errno; // failed to fork
30413        }
30414#endif
30415    }
30416    parent.status = parent.pid > 0 ? 0 : -1; // if didn't start, set error status
30417    return retval;
30418}
30419
30420// --- command.x2admin_proc.x2admin.StartRead
30421// Start subprocess & Read output
30422algo::Fildes command::x2admin_StartRead(command::x2admin_proc& parent, algo_lib::FFildes &read) {
30423    int pipefd[2];
30424    int rc=pipe(pipefd);
30425    (void)rc;
30426    read.fd.value = pipefd[0];
30427    parent.fstdout  << ">&" << pipefd[1];
30428    x2admin_Start(parent);
30429    (void)close(pipefd[1]);
30430    return read.fd;
30431}
30432
30433// --- command.x2admin_proc.x2admin.Kill
30434// Kill subprocess and wait
30435void command::x2admin_Kill(command::x2admin_proc& parent) {
30436    if (parent.pid != 0) {
30437        kill(parent.pid,9);
30438        x2admin_Wait(parent);
30439    }
30440}
30441
30442// --- command.x2admin_proc.x2admin.Wait
30443// Wait for subprocess to return
30444void command::x2admin_Wait(command::x2admin_proc& parent) {
30445    if (parent.pid > 0) {
30446        int wait_flags = 0;
30447        int wait_status = 0;
30448        int rc = -1;
30449        do {
30450            // really wait for subprocess to exit
30451            rc = waitpid(parent.pid,&wait_status,wait_flags);
30452        } while (rc==-1 && errno==EINTR);
30453        if (rc == parent.pid) {
30454            parent.status = wait_status;
30455            parent.pid = 0;
30456        }
30457    }
30458}
30459
30460// --- command.x2admin_proc.x2admin.Exec
30461// Start + Wait
30462// Execute subprocess and return exit code
30463int command::x2admin_Exec(command::x2admin_proc& parent) {
30464    x2admin_Start(parent);
30465    x2admin_Wait(parent);
30466    return parent.status;
30467}
30468
30469// --- command.x2admin_proc.x2admin.ExecX
30470// Start + Wait, throw exception on error
30471// Execute subprocess; throw human-readable exception on error
30472void command::x2admin_ExecX(command::x2admin_proc& parent) {
30473    int rc = x2admin_Exec(parent);
30474    vrfy(rc==0, tempstr() << "algo_lib.exec" << Keyval("cmd",x2admin_ToCmdline(parent))
30475    << Keyval("comment",algo::DescribeWaitStatus(parent.status)));
30476}
30477
30478// --- command.x2admin_proc.x2admin.Execv
30479// Call execv()
30480// Call execv with specified parameters
30481int command::x2admin_Execv(command::x2admin_proc& parent) {
30482    int ret = 0;
30483    algo::StringAry args;
30484    x2admin_ToArgv(parent, args);
30485    char **argv = (char**)alloca((ary_N(args)+1)*sizeof(*argv));
30486    ind_beg(algo::StringAry_ary_curs,arg,args) {
30487        argv[ind_curs(arg).index] = Zeroterm(arg);
30488    }ind_end;
30489    argv[ary_N(args)] = NULL;
30490    // if parent.path is relative, search for it in PATH
30491    algo_lib::ResolveExecFname(parent.path);
30492    ret = execv(Zeroterm(parent.path),argv);
30493    return ret;
30494}
30495
30496// --- command.x2admin_proc.x2admin.ToCmdline
30497algo::tempstr command::x2admin_ToCmdline(command::x2admin_proc& parent) {
30498    algo::tempstr retval;
30499    retval << parent.path << " ";
30500    command::x2admin_PrintArgv(parent.cmd,retval);
30501    if (ch_N(parent.fstdin)) {
30502        retval << " " << parent.fstdin;
30503    }
30504    if (ch_N(parent.fstdout)) {
30505        retval << " " << parent.fstdout;
30506    }
30507    if (ch_N(parent.fstderr)) {
30508        retval << " 2" << parent.fstderr;
30509    }
30510    return retval;
30511}
30512
30513// --- command.x2admin_proc.x2admin.ToArgv
30514// Form array from the command line
30515void command::x2admin_ToArgv(command::x2admin_proc& parent, algo::StringAry& args) {
30516    ary_RemoveAll(args);
30517    ary_Alloc(args) << parent.path;
30518
30519    if (parent.cmd.in != "data") {
30520        cstring *arg = &ary_Alloc(args);
30521        *arg << "-in:";
30522        cstring_Print(parent.cmd.in, *arg);
30523    }
30524
30525    if (parent.cmd.envnode.expr != "%") {
30526        cstring *arg = &ary_Alloc(args);
30527        *arg << "-envnode:";
30528        command::envnode_Print(parent.cmd, *arg);
30529    }
30530
30531    if (parent.cmd.admsvc.expr != "%") {
30532        cstring *arg = &ary_Alloc(args);
30533        *arg << "-admsvc:";
30534        command::admsvc_Print(parent.cmd, *arg);
30535    }
30536
30537    if (parent.cmd.generate != false) {
30538        cstring *arg = &ary_Alloc(args);
30539        *arg << "-generate:";
30540        bool_Print(parent.cmd.generate, *arg);
30541    }
30542
30543    if (parent.cmd.update != false) {
30544        cstring *arg = &ary_Alloc(args);
30545        *arg << "-update:";
30546        bool_Print(parent.cmd.update, *arg);
30547    }
30548
30549    if (parent.cmd.verify != false) {
30550        cstring *arg = &ary_Alloc(args);
30551        *arg << "-verify:";
30552        bool_Print(parent.cmd.verify, *arg);
30553    }
30554
30555    if (parent.cmd.show != false) {
30556        cstring *arg = &ary_Alloc(args);
30557        *arg << "-show:";
30558        bool_Print(parent.cmd.show, *arg);
30559    }
30560
30561    if (parent.cmd.dry_run != false) {
30562        cstring *arg = &ary_Alloc(args);
30563        *arg << "-dry_run:";
30564        bool_Print(parent.cmd.dry_run, *arg);
30565    }
30566    for (int i=1; i < algo_lib::_db.cmdline.verbose; ++i) {
30567        ary_Alloc(args) << "-verbose";
30568    }
30569}
30570
30571// --- command.x2admin_proc..Uninit
30572void command::x2admin_proc_Uninit(command::x2admin_proc& parent) {
30573    command::x2admin_proc &row = parent; (void)row;
30574
30575    // command.x2admin_proc.x2admin.Uninit (Exec)  //
30576    x2admin_Kill(parent); // kill child, ensure forward progress
30577}
30578
30579// --- command.x2cmt..ReadFieldMaybe
30580bool command::x2cmt_ReadFieldMaybe(command::x2cmt& parent, algo::strptr field, algo::strptr strval) {
30581    bool retval = true;
30582    command::FieldId field_id;
30583    (void)value_SetStrptrMaybe(field_id,field);
30584    switch(field_id) {
30585        case command_FieldId_in: {
30586            retval = algo::cstring_ReadStrptrMaybe(parent.in, strval);
30587            break;
30588        }
30589        case command_FieldId_prefix: {
30590            retval = algo::cstring_ReadStrptrMaybe(parent.prefix, strval);
30591            break;
30592        }
30593        default: break;
30594    }
30595    if (!retval) {
30596        algo_lib::AppendErrtext("attr",field);
30597    }
30598    return retval;
30599}
30600
30601// --- command.x2cmt..ReadTupleMaybe
30602// Read fields of command::x2cmt from attributes of ascii tuple TUPLE
30603bool command::x2cmt_ReadTupleMaybe(command::x2cmt &parent, algo::Tuple &tuple) {
30604    bool retval = true;
30605    ind_beg(algo::Tuple_attrs_curs,attr,tuple) {
30606        retval = x2cmt_ReadFieldMaybe(parent, attr.name, attr.value);
30607        if (!retval) {
30608            break;
30609        }
30610    }ind_end;
30611    return retval;
30612}
30613
30614// --- command.x2cmt..ToCmdline
30615// Convenience function that returns a full command line
30616// Assume command is in a directory called bin
30617tempstr command::x2cmt_ToCmdline(command::x2cmt& row) {
30618    tempstr ret;
30619    ret << "bin/x2cmt ";
30620    x2cmt_PrintArgv(row, ret);
30621    // inherit less intense verbose, debug options
30622    for (int i = 1; i < algo_lib::_db.cmdline.verbose; i++) {
30623        ret << " -verbose";
30624    }
30625    for (int i = 1; i < algo_lib::_db.cmdline.debug; i++) {
30626        ret << " -debug";
30627    }
30628    return ret;
30629}
30630
30631// --- command.x2cmt..PrintArgv
30632// print string representation of ROW to string STR
30633// cfmt:command.x2cmt.Argv  printfmt:Tuple
30634void command::x2cmt_PrintArgv(command::x2cmt& row, algo::cstring& str) {
30635    algo::tempstr temp;
30636    (void)temp;
30637    (void)str;
30638    if (!(row.in == "data")) {
30639        ch_RemoveAll(temp);
30640        cstring_Print(row.in, temp);
30641        str << " -in:";
30642        strptr_PrintBash(temp,str);
30643    }
30644    if (!(row.prefix == "")) {
30645        ch_RemoveAll(temp);
30646        cstring_Print(row.prefix, temp);
30647        str << " -prefix:";
30648        strptr_PrintBash(temp,str);
30649    }
30650}
30651
30652// --- command.x2cmt..NArgs
30653// Used with command lines
30654// Return # of command-line arguments that must follow this argument
30655// If FIELD is invalid, return -1
30656i32 command::x2cmt_NArgs(command::FieldId field, algo::strptr& out_dflt, bool* out_anon) {
30657    i32 retval = 1;
30658    switch (field) {
30659        case command_FieldId_in: { // $comment
30660            *out_anon = false;
30661        } break;
30662        case command_FieldId_prefix: { // $comment
30663            *out_anon = false;
30664        } break;
30665        default:
30666        retval=-1; // unrecognized
30667    }
30668    (void)out_dflt;//only to avoid -Wunused-parameter
30669    return retval;
30670}
30671
30672// --- command.x2cmt_proc.x2cmt.Start
30673// Start subprocess
30674// If subprocess already running, do nothing. Otherwise, start it
30675int command::x2cmt_Start(command::x2cmt_proc& parent) {
30676    int retval = 0;
30677    if (parent.pid == 0) {
30678        verblog(x2cmt_ToCmdline(parent)); // maybe print command
30679#ifdef WIN32
30680        algo_lib::ResolveExecFname(parent.path);
30681        tempstr cmdline(x2cmt_ToCmdline(parent));
30682        parent.pid = dospawn(Zeroterm(parent.path),Zeroterm(cmdline),parent.timeout,parent.fstdin,parent.fstdout,parent.fstderr);
30683#else
30684        parent.pid = fork();
30685        if (parent.pid == 0) { // child
30686            algo_lib::DieWithParent();
30687            if (parent.timeout > 0) {
30688                alarm(parent.timeout);
30689            }
30690            if (retval==0) retval=algo_lib::ApplyRedirect(parent.fstdin , 0);
30691            if (retval==0) retval=algo_lib::ApplyRedirect(parent.fstdout, 1);
30692            if (retval==0) retval=algo_lib::ApplyRedirect(parent.fstderr, 2);
30693            if (retval==0) retval= x2cmt_Execv(parent);
30694            if (retval != 0) { // if start fails, print error
30695                int err=errno;
30696                prerr("command.x2cmt_execv"
30697                <<Keyval("errno",err)
30698                <<Keyval("errstr",strerror(err))
30699                <<Keyval("comment","Execv failed"));
30700            }
30701            _exit(127); // if failed to start, exit anyway
30702        } else if (parent.pid == -1) {
30703            retval = errno; // failed to fork
30704        }
30705#endif
30706    }
30707    parent.status = parent.pid > 0 ? 0 : -1; // if didn't start, set error status
30708    return retval;
30709}
30710
30711// --- command.x2cmt_proc.x2cmt.StartRead
30712// Start subprocess & Read output
30713algo::Fildes command::x2cmt_StartRead(command::x2cmt_proc& parent, algo_lib::FFildes &read) {
30714    int pipefd[2];
30715    int rc=pipe(pipefd);
30716    (void)rc;
30717    read.fd.value = pipefd[0];
30718    parent.fstdout  << ">&" << pipefd[1];
30719    x2cmt_Start(parent);
30720    (void)close(pipefd[1]);
30721    return read.fd;
30722}
30723
30724// --- command.x2cmt_proc.x2cmt.Kill
30725// Kill subprocess and wait
30726void command::x2cmt_Kill(command::x2cmt_proc& parent) {
30727    if (parent.pid != 0) {
30728        kill(parent.pid,9);
30729        x2cmt_Wait(parent);
30730    }
30731}
30732
30733// --- command.x2cmt_proc.x2cmt.Wait
30734// Wait for subprocess to return
30735void command::x2cmt_Wait(command::x2cmt_proc& parent) {
30736    if (parent.pid > 0) {
30737        int wait_flags = 0;
30738        int wait_status = 0;
30739        int rc = -1;
30740        do {
30741            // really wait for subprocess to exit
30742            rc = waitpid(parent.pid,&wait_status,wait_flags);
30743        } while (rc==-1 && errno==EINTR);
30744        if (rc == parent.pid) {
30745            parent.status = wait_status;
30746            parent.pid = 0;
30747        }
30748    }
30749}
30750
30751// --- command.x2cmt_proc.x2cmt.Exec
30752// Start + Wait
30753// Execute subprocess and return exit code
30754int command::x2cmt_Exec(command::x2cmt_proc& parent) {
30755    x2cmt_Start(parent);
30756    x2cmt_Wait(parent);
30757    return parent.status;
30758}
30759
30760// --- command.x2cmt_proc.x2cmt.ExecX
30761// Start + Wait, throw exception on error
30762// Execute subprocess; throw human-readable exception on error
30763void command::x2cmt_ExecX(command::x2cmt_proc& parent) {
30764    int rc = x2cmt_Exec(parent);
30765    vrfy(rc==0, tempstr() << "algo_lib.exec" << Keyval("cmd",x2cmt_ToCmdline(parent))
30766    << Keyval("comment",algo::DescribeWaitStatus(parent.status)));
30767}
30768
30769// --- command.x2cmt_proc.x2cmt.Execv
30770// Call execv()
30771// Call execv with specified parameters
30772int command::x2cmt_Execv(command::x2cmt_proc& parent) {
30773    int ret = 0;
30774    algo::StringAry args;
30775    x2cmt_ToArgv(parent, args);
30776    char **argv = (char**)alloca((ary_N(args)+1)*sizeof(*argv));
30777    ind_beg(algo::StringAry_ary_curs,arg,args) {
30778        argv[ind_curs(arg).index] = Zeroterm(arg);
30779    }ind_end;
30780    argv[ary_N(args)] = NULL;
30781    // if parent.path is relative, search for it in PATH
30782    algo_lib::ResolveExecFname(parent.path);
30783    ret = execv(Zeroterm(parent.path),argv);
30784    return ret;
30785}
30786
30787// --- command.x2cmt_proc.x2cmt.ToCmdline
30788algo::tempstr command::x2cmt_ToCmdline(command::x2cmt_proc& parent) {
30789    algo::tempstr retval;
30790    retval << parent.path << " ";
30791    command::x2cmt_PrintArgv(parent.cmd,retval);
30792    if (ch_N(parent.fstdin)) {
30793        retval << " " << parent.fstdin;
30794    }
30795    if (ch_N(parent.fstdout)) {
30796        retval << " " << parent.fstdout;
30797    }
30798    if (ch_N(parent.fstderr)) {
30799        retval << " 2" << parent.fstderr;
30800    }
30801    return retval;
30802}
30803
30804// --- command.x2cmt_proc.x2cmt.ToArgv
30805// Form array from the command line
30806void command::x2cmt_ToArgv(command::x2cmt_proc& parent, algo::StringAry& args) {
30807    ary_RemoveAll(args);
30808    ary_Alloc(args) << parent.path;
30809
30810    if (parent.cmd.in != "data") {
30811        cstring *arg = &ary_Alloc(args);
30812        *arg << "-in:";
30813        cstring_Print(parent.cmd.in, *arg);
30814    }
30815
30816    if (parent.cmd.prefix != "") {
30817        cstring *arg = &ary_Alloc(args);
30818        *arg << "-prefix:";
30819        cstring_Print(parent.cmd.prefix, *arg);
30820    }
30821    for (int i=1; i < algo_lib::_db.cmdline.verbose; ++i) {
30822        ary_Alloc(args) << "-verbose";
30823    }
30824}
30825
30826// --- command.x2cmt_proc..Uninit
30827void command::x2cmt_proc_Uninit(command::x2cmt_proc& parent) {
30828    command::x2cmt_proc &row = parent; (void)row;
30829
30830    // command.x2cmt_proc.x2cmt.Uninit (Exec)  //
30831    x2cmt_Kill(parent); // kill child, ensure forward progress
30832}
30833
30834// --- command.x2gf..ReadFieldMaybe
30835bool command::x2gf_ReadFieldMaybe(command::x2gf& parent, algo::strptr field, algo::strptr strval) {
30836    bool retval = true;
30837    command::FieldId field_id;
30838    (void)value_SetStrptrMaybe(field_id,field);
30839    switch(field_id) {
30840        case command_FieldId_in: {
30841            retval = algo::cstring_ReadStrptrMaybe(parent.in, strval);
30842            break;
30843        }
30844        case command_FieldId_prefix: {
30845            retval = algo::cstring_ReadStrptrMaybe(parent.prefix, strval);
30846            break;
30847        }
30848        default: break;
30849    }
30850    if (!retval) {
30851        algo_lib::AppendErrtext("attr",field);
30852    }
30853    return retval;
30854}
30855
30856// --- command.x2gf..ReadTupleMaybe
30857// Read fields of command::x2gf from attributes of ascii tuple TUPLE
30858bool command::x2gf_ReadTupleMaybe(command::x2gf &parent, algo::Tuple &tuple) {
30859    bool retval = true;
30860    ind_beg(algo::Tuple_attrs_curs,attr,tuple) {
30861        retval = x2gf_ReadFieldMaybe(parent, attr.name, attr.value);
30862        if (!retval) {
30863            break;
30864        }
30865    }ind_end;
30866    return retval;
30867}
30868
30869// --- command.x2gf..ToCmdline
30870// Convenience function that returns a full command line
30871// Assume command is in a directory called bin
30872tempstr command::x2gf_ToCmdline(command::x2gf& row) {
30873    tempstr ret;
30874    ret << "bin/x2gf ";
30875    x2gf_PrintArgv(row, ret);
30876    // inherit less intense verbose, debug options
30877    for (int i = 1; i < algo_lib::_db.cmdline.verbose; i++) {
30878        ret << " -verbose";
30879    }
30880    for (int i = 1; i < algo_lib::_db.cmdline.debug; i++) {
30881        ret << " -debug";
30882    }
30883    return ret;
30884}
30885
30886// --- command.x2gf..PrintArgv
30887// print string representation of ROW to string STR
30888// cfmt:command.x2gf.Argv  printfmt:Tuple
30889void command::x2gf_PrintArgv(command::x2gf& row, algo::cstring& str) {
30890    algo::tempstr temp;
30891    (void)temp;
30892    (void)str;
30893    if (!(row.in == "data")) {
30894        ch_RemoveAll(temp);
30895        cstring_Print(row.in, temp);
30896        str << " -in:";
30897        strptr_PrintBash(temp,str);
30898    }
30899    if (!(row.prefix == "")) {
30900        ch_RemoveAll(temp);
30901        cstring_Print(row.prefix, temp);
30902        str << " -prefix:";
30903        strptr_PrintBash(temp,str);
30904    }
30905}
30906
30907// --- command.x2gf..NArgs
30908// Used with command lines
30909// Return # of command-line arguments that must follow this argument
30910// If FIELD is invalid, return -1
30911i32 command::x2gf_NArgs(command::FieldId field, algo::strptr& out_dflt, bool* out_anon) {
30912    i32 retval = 1;
30913    switch (field) {
30914        case command_FieldId_in: { // $comment
30915            *out_anon = false;
30916        } break;
30917        case command_FieldId_prefix: { // $comment
30918            *out_anon = false;
30919        } break;
30920        default:
30921        retval=-1; // unrecognized
30922    }
30923    (void)out_dflt;//only to avoid -Wunused-parameter
30924    return retval;
30925}
30926
30927// --- command.x2gf_proc.x2gf.Start
30928// Start subprocess
30929// If subprocess already running, do nothing. Otherwise, start it
30930int command::x2gf_Start(command::x2gf_proc& parent) {
30931    int retval = 0;
30932    if (parent.pid == 0) {
30933        verblog(x2gf_ToCmdline(parent)); // maybe print command
30934#ifdef WIN32
30935        algo_lib::ResolveExecFname(parent.path);
30936        tempstr cmdline(x2gf_ToCmdline(parent));
30937        parent.pid = dospawn(Zeroterm(parent.path),Zeroterm(cmdline),parent.timeout,parent.fstdin,parent.fstdout,parent.fstderr);
30938#else
30939        parent.pid = fork();
30940        if (parent.pid == 0) { // child
30941            algo_lib::DieWithParent();
30942            if (parent.timeout > 0) {
30943                alarm(parent.timeout);
30944            }
30945            if (retval==0) retval=algo_lib::ApplyRedirect(parent.fstdin , 0);
30946            if (retval==0) retval=algo_lib::ApplyRedirect(parent.fstdout, 1);
30947            if (retval==0) retval=algo_lib::ApplyRedirect(parent.fstderr, 2);
30948            if (retval==0) retval= x2gf_Execv(parent);
30949            if (retval != 0) { // if start fails, print error
30950                int err=errno;
30951                prerr("command.x2gf_execv"
30952                <<Keyval("errno",err)
30953                <<Keyval("errstr",strerror(err))
30954                <<Keyval("comment","Execv failed"));
30955            }
30956            _exit(127); // if failed to start, exit anyway
30957        } else if (parent.pid == -1) {
30958            retval = errno; // failed to fork
30959        }
30960#endif
30961    }
30962    parent.status = parent.pid > 0 ? 0 : -1; // if didn't start, set error status
30963    return retval;
30964}
30965
30966// --- command.x2gf_proc.x2gf.StartRead
30967// Start subprocess & Read output
30968algo::Fildes command::x2gf_StartRead(command::x2gf_proc& parent, algo_lib::FFildes &read) {
30969    int pipefd[2];
30970    int rc=pipe(pipefd);
30971    (void)rc;
30972    read.fd.value = pipefd[0];
30973    parent.fstdout  << ">&" << pipefd[1];
30974    x2gf_Start(parent);
30975    (void)close(pipefd[1]);
30976    return read.fd;
30977}
30978
30979// --- command.x2gf_proc.x2gf.Kill
30980// Kill subprocess and wait
30981void command::x2gf_Kill(command::x2gf_proc& parent) {
30982    if (parent.pid != 0) {
30983        kill(parent.pid,9);
30984        x2gf_Wait(parent);
30985    }
30986}
30987
30988// --- command.x2gf_proc.x2gf.Wait
30989// Wait for subprocess to return
30990void command::x2gf_Wait(command::x2gf_proc& parent) {
30991    if (parent.pid > 0) {
30992        int wait_flags = 0;
30993        int wait_status = 0;
30994        int rc = -1;
30995        do {
30996            // really wait for subprocess to exit
30997            rc = waitpid(parent.pid,&wait_status,wait_flags);
30998        } while (rc==-1 && errno==EINTR);
30999        if (rc == parent.pid) {
31000            parent.status = wait_status;
31001            parent.pid = 0;
31002        }
31003    }
31004}
31005
31006// --- command.x2gf_proc.x2gf.Exec
31007// Start + Wait
31008// Execute subprocess and return exit code
31009int command::x2gf_Exec(command::x2gf_proc& parent) {
31010    x2gf_Start(parent);
31011    x2gf_Wait(parent);
31012    return parent.status;
31013}
31014
31015// --- command.x2gf_proc.x2gf.ExecX
31016// Start + Wait, throw exception on error
31017// Execute subprocess; throw human-readable exception on error
31018void command::x2gf_ExecX(command::x2gf_proc& parent) {
31019    int rc = x2gf_Exec(parent);
31020    vrfy(rc==0, tempstr() << "algo_lib.exec" << Keyval("cmd",x2gf_ToCmdline(parent))
31021    << Keyval("comment",algo::DescribeWaitStatus(parent.status)));
31022}
31023
31024// --- command.x2gf_proc.x2gf.Execv
31025// Call execv()
31026// Call execv with specified parameters
31027int command::x2gf_Execv(command::x2gf_proc& parent) {
31028    int ret = 0;
31029    algo::StringAry args;
31030    x2gf_ToArgv(parent, args);
31031    char **argv = (char**)alloca((ary_N(args)+1)*sizeof(*argv));
31032    ind_beg(algo::StringAry_ary_curs,arg,args) {
31033        argv[ind_curs(arg).index] = Zeroterm(arg);
31034    }ind_end;
31035    argv[ary_N(args)] = NULL;
31036    // if parent.path is relative, search for it in PATH
31037    algo_lib::ResolveExecFname(parent.path);
31038    ret = execv(Zeroterm(parent.path),argv);
31039    return ret;
31040}
31041
31042// --- command.x2gf_proc.x2gf.ToCmdline
31043algo::tempstr command::x2gf_ToCmdline(command::x2gf_proc& parent) {
31044    algo::tempstr retval;
31045    retval << parent.path << " ";
31046    command::x2gf_PrintArgv(parent.cmd,retval);
31047    if (ch_N(parent.fstdin)) {
31048        retval << " " << parent.fstdin;
31049    }
31050    if (ch_N(parent.fstdout)) {
31051        retval << " " << parent.fstdout;
31052    }
31053    if (ch_N(parent.fstderr)) {
31054        retval << " 2" << parent.fstderr;
31055    }
31056    return retval;
31057}
31058
31059// --- command.x2gf_proc.x2gf.ToArgv
31060// Form array from the command line
31061void command::x2gf_ToArgv(command::x2gf_proc& parent, algo::StringAry& args) {
31062    ary_RemoveAll(args);
31063    ary_Alloc(args) << parent.path;
31064
31065    if (parent.cmd.in != "data") {
31066        cstring *arg = &ary_Alloc(args);
31067        *arg << "-in:";
31068        cstring_Print(parent.cmd.in, *arg);
31069    }
31070
31071    if (parent.cmd.prefix != "") {
31072        cstring *arg = &ary_Alloc(args);
31073        *arg << "-prefix:";
31074        cstring_Print(parent.cmd.prefix, *arg);
31075    }
31076    for (int i=1; i < algo_lib::_db.cmdline.verbose; ++i) {
31077        ary_Alloc(args) << "-verbose";
31078    }
31079}
31080
31081// --- command.x2gf_proc..Uninit
31082void command::x2gf_proc_Uninit(command::x2gf_proc& parent) {
31083    command::x2gf_proc &row = parent; (void)row;
31084
31085    // command.x2gf_proc.x2gf.Uninit (Exec)  //
31086    x2gf_Kill(parent); // kill child, ensure forward progress
31087}
31088
31089// --- command.x2gw..ReadFieldMaybe
31090bool command::x2gw_ReadFieldMaybe(command::x2gw& parent, algo::strptr field, algo::strptr strval) {
31091    bool retval = true;
31092    command::FieldId field_id;
31093    (void)value_SetStrptrMaybe(field_id,field);
31094    switch(field_id) {
31095        case command_FieldId_in: {
31096            retval = algo::cstring_ReadStrptrMaybe(parent.in, strval);
31097            break;
31098        }
31099        case command_FieldId_prefix: {
31100            retval = algo::cstring_ReadStrptrMaybe(parent.prefix, strval);
31101            break;
31102        }
31103        default: break;
31104    }
31105    if (!retval) {
31106        algo_lib::AppendErrtext("attr",field);
31107    }
31108    return retval;
31109}
31110
31111// --- command.x2gw..ReadTupleMaybe
31112// Read fields of command::x2gw from attributes of ascii tuple TUPLE
31113bool command::x2gw_ReadTupleMaybe(command::x2gw &parent, algo::Tuple &tuple) {
31114    bool retval = true;
31115    ind_beg(algo::Tuple_attrs_curs,attr,tuple) {
31116        retval = x2gw_ReadFieldMaybe(parent, attr.name, attr.value);
31117        if (!retval) {
31118            break;
31119        }
31120    }ind_end;
31121    return retval;
31122}
31123
31124// --- command.x2gw..ToCmdline
31125// Convenience function that returns a full command line
31126// Assume command is in a directory called bin
31127tempstr command::x2gw_ToCmdline(command::x2gw& row) {
31128    tempstr ret;
31129    ret << "bin/x2gw ";
31130    x2gw_PrintArgv(row, ret);
31131    // inherit less intense verbose, debug options
31132    for (int i = 1; i < algo_lib::_db.cmdline.verbose; i++) {
31133        ret << " -verbose";
31134    }
31135    for (int i = 1; i < algo_lib::_db.cmdline.debug; i++) {
31136        ret << " -debug";
31137    }
31138    return ret;
31139}
31140
31141// --- command.x2gw..PrintArgv
31142// print string representation of ROW to string STR
31143// cfmt:command.x2gw.Argv  printfmt:Tuple
31144void command::x2gw_PrintArgv(command::x2gw& row, algo::cstring& str) {
31145    algo::tempstr temp;
31146    (void)temp;
31147    (void)str;
31148    if (!(row.in == "data")) {
31149        ch_RemoveAll(temp);
31150        cstring_Print(row.in, temp);
31151        str << " -in:";
31152        strptr_PrintBash(temp,str);
31153    }
31154    if (!(row.prefix == "")) {
31155        ch_RemoveAll(temp);
31156        cstring_Print(row.prefix, temp);
31157        str << " -prefix:";
31158        strptr_PrintBash(temp,str);
31159    }
31160}
31161
31162// --- command.x2gw..NArgs
31163// Used with command lines
31164// Return # of command-line arguments that must follow this argument
31165// If FIELD is invalid, return -1
31166i32 command::x2gw_NArgs(command::FieldId field, algo::strptr& out_dflt, bool* out_anon) {
31167    i32 retval = 1;
31168    switch (field) {
31169        case command_FieldId_in: { // $comment
31170            *out_anon = false;
31171        } break;
31172        case command_FieldId_prefix: { // $comment
31173            *out_anon = false;
31174        } break;
31175        default:
31176        retval=-1; // unrecognized
31177    }
31178    (void)out_dflt;//only to avoid -Wunused-parameter
31179    return retval;
31180}
31181
31182// --- command.x2gw_proc.x2gw.Start
31183// Start subprocess
31184// If subprocess already running, do nothing. Otherwise, start it
31185int command::x2gw_Start(command::x2gw_proc& parent) {
31186    int retval = 0;
31187    if (parent.pid == 0) {
31188        verblog(x2gw_ToCmdline(parent)); // maybe print command
31189#ifdef WIN32
31190        algo_lib::ResolveExecFname(parent.path);
31191        tempstr cmdline(x2gw_ToCmdline(parent));
31192        parent.pid = dospawn(Zeroterm(parent.path),Zeroterm(cmdline),parent.timeout,parent.fstdin,parent.fstdout,parent.fstderr);
31193#else
31194        parent.pid = fork();
31195        if (parent.pid == 0) { // child
31196            algo_lib::DieWithParent();
31197            if (parent.timeout > 0) {
31198                alarm(parent.timeout);
31199            }
31200            if (retval==0) retval=algo_lib::ApplyRedirect(parent.fstdin , 0);
31201            if (retval==0) retval=algo_lib::ApplyRedirect(parent.fstdout, 1);
31202            if (retval==0) retval=algo_lib::ApplyRedirect(parent.fstderr, 2);
31203            if (retval==0) retval= x2gw_Execv(parent);
31204            if (retval != 0) { // if start fails, print error
31205                int err=errno;
31206                prerr("command.x2gw_execv"
31207                <<Keyval("errno",err)
31208                <<Keyval("errstr",strerror(err))
31209                <<Keyval("comment","Execv failed"));
31210            }
31211            _exit(127); // if failed to start, exit anyway
31212        } else if (parent.pid == -1) {
31213            retval = errno; // failed to fork
31214        }
31215#endif
31216    }
31217    parent.status = parent.pid > 0 ? 0 : -1; // if didn't start, set error status
31218    return retval;
31219}
31220
31221// --- command.x2gw_proc.x2gw.StartRead
31222// Start subprocess & Read output
31223algo::Fildes command::x2gw_StartRead(command::x2gw_proc& parent, algo_lib::FFildes &read) {
31224    int pipefd[2];
31225    int rc=pipe(pipefd);
31226    (void)rc;
31227    read.fd.value = pipefd[0];
31228    parent.fstdout  << ">&" << pipefd[1];
31229    x2gw_Start(parent);
31230    (void)close(pipefd[1]);
31231    return read.fd;
31232}
31233
31234// --- command.x2gw_proc.x2gw.Kill
31235// Kill subprocess and wait
31236void command::x2gw_Kill(command::x2gw_proc& parent) {
31237    if (parent.pid != 0) {
31238        kill(parent.pid,9);
31239        x2gw_Wait(parent);
31240    }
31241}
31242
31243// --- command.x2gw_proc.x2gw.Wait
31244// Wait for subprocess to return
31245void command::x2gw_Wait(command::x2gw_proc& parent) {
31246    if (parent.pid > 0) {
31247        int wait_flags = 0;
31248        int wait_status = 0;
31249        int rc = -1;
31250        do {
31251            // really wait for subprocess to exit
31252            rc = waitpid(parent.pid,&wait_status,wait_flags);
31253        } while (rc==-1 && errno==EINTR);
31254        if (rc == parent.pid) {
31255            parent.status = wait_status;
31256            parent.pid = 0;
31257        }
31258    }
31259}
31260
31261// --- command.x2gw_proc.x2gw.Exec
31262// Start + Wait
31263// Execute subprocess and return exit code
31264int command::x2gw_Exec(command::x2gw_proc& parent) {
31265    x2gw_Start(parent);
31266    x2gw_Wait(parent);
31267    return parent.status;
31268}
31269
31270// --- command.x2gw_proc.x2gw.ExecX
31271// Start + Wait, throw exception on error
31272// Execute subprocess; throw human-readable exception on error
31273void command::x2gw_ExecX(command::x2gw_proc& parent) {
31274    int rc = x2gw_Exec(parent);
31275    vrfy(rc==0, tempstr() << "algo_lib.exec" << Keyval("cmd",x2gw_ToCmdline(parent))
31276    << Keyval("comment",algo::DescribeWaitStatus(parent.status)));
31277}
31278
31279// --- command.x2gw_proc.x2gw.Execv
31280// Call execv()
31281// Call execv with specified parameters
31282int command::x2gw_Execv(command::x2gw_proc& parent) {
31283    int ret = 0;
31284    algo::StringAry args;
31285    x2gw_ToArgv(parent, args);
31286    char **argv = (char**)alloca((ary_N(args)+1)*sizeof(*argv));
31287    ind_beg(algo::StringAry_ary_curs,arg,args) {
31288        argv[ind_curs(arg).index] = Zeroterm(arg);
31289    }ind_end;
31290    argv[ary_N(args)] = NULL;
31291    // if parent.path is relative, search for it in PATH
31292    algo_lib::ResolveExecFname(parent.path);
31293    ret = execv(Zeroterm(parent.path),argv);
31294    return ret;
31295}
31296
31297// --- command.x2gw_proc.x2gw.ToCmdline
31298algo::tempstr command::x2gw_ToCmdline(command::x2gw_proc& parent) {
31299    algo::tempstr retval;
31300    retval << parent.path << " ";
31301    command::x2gw_PrintArgv(parent.cmd,retval);
31302    if (ch_N(parent.fstdin)) {
31303        retval << " " << parent.fstdin;
31304    }
31305    if (ch_N(parent.fstdout)) {
31306        retval << " " << parent.fstdout;
31307    }
31308    if (ch_N(parent.fstderr)) {
31309        retval << " 2" << parent.fstderr;
31310    }
31311    return retval;
31312}
31313
31314// --- command.x2gw_proc.x2gw.ToArgv
31315// Form array from the command line
31316void command::x2gw_ToArgv(command::x2gw_proc& parent, algo::StringAry& args) {
31317    ary_RemoveAll(args);
31318    ary_Alloc(args) << parent.path;
31319
31320    if (parent.cmd.in != "data") {
31321        cstring *arg = &ary_Alloc(args);
31322        *arg << "-in:";
31323        cstring_Print(parent.cmd.in, *arg);
31324    }
31325
31326    if (parent.cmd.prefix != "") {
31327        cstring *arg = &ary_Alloc(args);
31328        *arg << "-prefix:";
31329        cstring_Print(parent.cmd.prefix, *arg);
31330    }
31331    for (int i=1; i < algo_lib::_db.cmdline.verbose; ++i) {
31332        ary_Alloc(args) << "-verbose";
31333    }
31334}
31335
31336// --- command.x2gw_proc..Uninit
31337void command::x2gw_proc_Uninit(command::x2gw_proc& parent) {
31338    command::x2gw_proc &row = parent; (void)row;
31339
31340    // command.x2gw_proc.x2gw.Uninit (Exec)  //
31341    x2gw_Kill(parent); // kill child, ensure forward progress
31342}
31343
31344// --- command.x2mon..ReadFieldMaybe
31345bool command::x2mon_ReadFieldMaybe(command::x2mon& parent, algo::strptr field, algo::strptr strval) {
31346    bool retval = true;
31347    command::FieldId field_id;
31348    (void)value_SetStrptrMaybe(field_id,field);
31349    switch(field_id) {
31350        case command_FieldId_in: {
31351            retval = algo::cstring_ReadStrptrMaybe(parent.in, strval);
31352            break;
31353        }
31354        case command_FieldId_prefix: {
31355            retval = algo::cstring_ReadStrptrMaybe(parent.prefix, strval);
31356            break;
31357        }
31358        default: break;
31359    }
31360    if (!retval) {
31361        algo_lib::AppendErrtext("attr",field);
31362    }
31363    return retval;
31364}
31365
31366// --- command.x2mon..ReadTupleMaybe
31367// Read fields of command::x2mon from attributes of ascii tuple TUPLE
31368bool command::x2mon_ReadTupleMaybe(command::x2mon &parent, algo::Tuple &tuple) {
31369    bool retval = true;
31370    ind_beg(algo::Tuple_attrs_curs,attr,tuple) {
31371        retval = x2mon_ReadFieldMaybe(parent, attr.name, attr.value);
31372        if (!retval) {
31373            break;
31374        }
31375    }ind_end;
31376    return retval;
31377}
31378
31379// --- command.x2mon..ToCmdline
31380// Convenience function that returns a full command line
31381// Assume command is in a directory called bin
31382tempstr command::x2mon_ToCmdline(command::x2mon& row) {
31383    tempstr ret;
31384    ret << "bin/x2mon ";
31385    x2mon_PrintArgv(row, ret);
31386    // inherit less intense verbose, debug options
31387    for (int i = 1; i < algo_lib::_db.cmdline.verbose; i++) {
31388        ret << " -verbose";
31389    }
31390    for (int i = 1; i < algo_lib::_db.cmdline.debug; i++) {
31391        ret << " -debug";
31392    }
31393    return ret;
31394}
31395
31396// --- command.x2mon..PrintArgv
31397// print string representation of ROW to string STR
31398// cfmt:command.x2mon.Argv  printfmt:Tuple
31399void command::x2mon_PrintArgv(command::x2mon& row, algo::cstring& str) {
31400    algo::tempstr temp;
31401    (void)temp;
31402    (void)str;
31403    if (!(row.in == "data")) {
31404        ch_RemoveAll(temp);
31405        cstring_Print(row.in, temp);
31406        str << " -in:";
31407        strptr_PrintBash(temp,str);
31408    }
31409    if (!(row.prefix == "")) {
31410        ch_RemoveAll(temp);
31411        cstring_Print(row.prefix, temp);
31412        str << " -prefix:";
31413        strptr_PrintBash(temp,str);
31414    }
31415}
31416
31417// --- command.x2mon..NArgs
31418// Used with command lines
31419// Return # of command-line arguments that must follow this argument
31420// If FIELD is invalid, return -1
31421i32 command::x2mon_NArgs(command::FieldId field, algo::strptr& out_dflt, bool* out_anon) {
31422    i32 retval = 1;
31423    switch (field) {
31424        case command_FieldId_in: { // $comment
31425            *out_anon = false;
31426        } break;
31427        case command_FieldId_prefix: { // $comment
31428            *out_anon = false;
31429        } break;
31430        default:
31431        retval=-1; // unrecognized
31432    }
31433    (void)out_dflt;//only to avoid -Wunused-parameter
31434    return retval;
31435}
31436
31437// --- command.x2mon_proc.x2mon.Start
31438// Start subprocess
31439// If subprocess already running, do nothing. Otherwise, start it
31440int command::x2mon_Start(command::x2mon_proc& parent) {
31441    int retval = 0;
31442    if (parent.pid == 0) {
31443        verblog(x2mon_ToCmdline(parent)); // maybe print command
31444#ifdef WIN32
31445        algo_lib::ResolveExecFname(parent.path);
31446        tempstr cmdline(x2mon_ToCmdline(parent));
31447        parent.pid = dospawn(Zeroterm(parent.path),Zeroterm(cmdline),parent.timeout,parent.fstdin,parent.fstdout,parent.fstderr);
31448#else
31449        parent.pid = fork();
31450        if (parent.pid == 0) { // child
31451            algo_lib::DieWithParent();
31452            if (parent.timeout > 0) {
31453                alarm(parent.timeout);
31454            }
31455            if (retval==0) retval=algo_lib::ApplyRedirect(parent.fstdin , 0);
31456            if (retval==0) retval=algo_lib::ApplyRedirect(parent.fstdout, 1);
31457            if (retval==0) retval=algo_lib::ApplyRedirect(parent.fstderr, 2);
31458            if (retval==0) retval= x2mon_Execv(parent);
31459            if (retval != 0) { // if start fails, print error
31460                int err=errno;
31461                prerr("command.x2mon_execv"
31462                <<Keyval("errno",err)
31463                <<Keyval("errstr",strerror(err))
31464                <<Keyval("comment","Execv failed"));
31465            }
31466            _exit(127); // if failed to start, exit anyway
31467        } else if (parent.pid == -1) {
31468            retval = errno; // failed to fork
31469        }
31470#endif
31471    }
31472    parent.status = parent.pid > 0 ? 0 : -1; // if didn't start, set error status
31473    return retval;
31474}
31475
31476// --- command.x2mon_proc.x2mon.StartRead
31477// Start subprocess & Read output
31478algo::Fildes command::x2mon_StartRead(command::x2mon_proc& parent, algo_lib::FFildes &read) {
31479    int pipefd[2];
31480    int rc=pipe(pipefd);
31481    (void)rc;
31482    read.fd.value = pipefd[0];
31483    parent.fstdout  << ">&" << pipefd[1];
31484    x2mon_Start(parent);
31485    (void)close(pipefd[1]);
31486    return read.fd;
31487}
31488
31489// --- command.x2mon_proc.x2mon.Kill
31490// Kill subprocess and wait
31491void command::x2mon_Kill(command::x2mon_proc& parent) {
31492    if (parent.pid != 0) {
31493        kill(parent.pid,9);
31494        x2mon_Wait(parent);
31495    }
31496}
31497
31498// --- command.x2mon_proc.x2mon.Wait
31499// Wait for subprocess to return
31500void command::x2mon_Wait(command::x2mon_proc& parent) {
31501    if (parent.pid > 0) {
31502        int wait_flags = 0;
31503        int wait_status = 0;
31504        int rc = -1;
31505        do {
31506            // really wait for subprocess to exit
31507            rc = waitpid(parent.pid,&wait_status,wait_flags);
31508        } while (rc==-1 && errno==EINTR);
31509        if (rc == parent.pid) {
31510            parent.status = wait_status;
31511            parent.pid = 0;
31512        }
31513    }
31514}
31515
31516// --- command.x2mon_proc.x2mon.Exec
31517// Start + Wait
31518// Execute subprocess and return exit code
31519int command::x2mon_Exec(command::x2mon_proc& parent) {
31520    x2mon_Start(parent);
31521    x2mon_Wait(parent);
31522    return parent.status;
31523}
31524
31525// --- command.x2mon_proc.x2mon.ExecX
31526// Start + Wait, throw exception on error
31527// Execute subprocess; throw human-readable exception on error
31528void command::x2mon_ExecX(command::x2mon_proc& parent) {
31529    int rc = x2mon_Exec(parent);
31530    vrfy(rc==0, tempstr() << "algo_lib.exec" << Keyval("cmd",x2mon_ToCmdline(parent))
31531    << Keyval("comment",algo::DescribeWaitStatus(parent.status)));
31532}
31533
31534// --- command.x2mon_proc.x2mon.Execv
31535// Call execv()
31536// Call execv with specified parameters
31537int command::x2mon_Execv(command::x2mon_proc& parent) {
31538    int ret = 0;
31539    algo::StringAry args;
31540    x2mon_ToArgv(parent, args);
31541    char **argv = (char**)alloca((ary_N(args)+1)*sizeof(*argv));
31542    ind_beg(algo::StringAry_ary_curs,arg,args) {
31543        argv[ind_curs(arg).index] = Zeroterm(arg);
31544    }ind_end;
31545    argv[ary_N(args)] = NULL;
31546    // if parent.path is relative, search for it in PATH
31547    algo_lib::ResolveExecFname(parent.path);
31548    ret = execv(Zeroterm(parent.path),argv);
31549    return ret;
31550}
31551
31552// --- command.x2mon_proc.x2mon.ToCmdline
31553algo::tempstr command::x2mon_ToCmdline(command::x2mon_proc& parent) {
31554    algo::tempstr retval;
31555    retval << parent.path << " ";
31556    command::x2mon_PrintArgv(parent.cmd,retval);
31557    if (ch_N(parent.fstdin)) {
31558        retval << " " << parent.fstdin;
31559    }
31560    if (ch_N(parent.fstdout)) {
31561        retval << " " << parent.fstdout;
31562    }
31563    if (ch_N(parent.fstderr)) {
31564        retval << " 2" << parent.fstderr;
31565    }
31566    return retval;
31567}
31568
31569// --- command.x2mon_proc.x2mon.ToArgv
31570// Form array from the command line
31571void command::x2mon_ToArgv(command::x2mon_proc& parent, algo::StringAry& args) {
31572    ary_RemoveAll(args);
31573    ary_Alloc(args) << parent.path;
31574
31575    if (parent.cmd.in != "data") {
31576        cstring *arg = &ary_Alloc(args);
31577        *arg << "-in:";
31578        cstring_Print(parent.cmd.in, *arg);
31579    }
31580
31581    if (parent.cmd.prefix != "") {
31582        cstring *arg = &ary_Alloc(args);
31583        *arg << "-prefix:";
31584        cstring_Print(parent.cmd.prefix, *arg);
31585    }
31586    for (int i=1; i < algo_lib::_db.cmdline.verbose; ++i) {
31587        ary_Alloc(args) << "-verbose";
31588    }
31589}
31590
31591// --- command.x2mon_proc..Uninit
31592void command::x2mon_proc_Uninit(command::x2mon_proc& parent) {
31593    command::x2mon_proc &row = parent; (void)row;
31594
31595    // command.x2mon_proc.x2mon.Uninit (Exec)  //
31596    x2mon_Kill(parent); // kill child, ensure forward progress
31597}
31598
31599// --- command.x2node.envnode.Print
31600// Print back to string
31601void command::envnode_Print(command::x2node& parent, algo::cstring &out) {
31602    Regx_Print(parent.envnode, out);
31603}
31604
31605// --- command.x2node.envnode.ReadStrptrMaybe
31606// Read Regx from string
31607// Convert string to field. Return success value
31608bool command::envnode_ReadStrptrMaybe(command::x2node& parent, algo::strptr in) {
31609    bool retval = true;
31610    Regx_ReadSql(parent.envnode, in, true);
31611    return retval;
31612}
31613
31614// --- command.x2node..ReadFieldMaybe
31615bool command::x2node_ReadFieldMaybe(command::x2node& parent, algo::strptr field, algo::strptr strval) {
31616    bool retval = true;
31617    command::FieldId field_id;
31618    (void)value_SetStrptrMaybe(field_id,field);
31619    switch(field_id) {
31620        case command_FieldId_in: {
31621            retval = algo::cstring_ReadStrptrMaybe(parent.in, strval);
31622            break;
31623        }
31624        case command_FieldId_envnode: {
31625            retval = envnode_ReadStrptrMaybe(parent, strval);
31626            break;
31627        }
31628        case command_FieldId_root: {
31629            retval = bool_ReadStrptrMaybe(parent.root, strval);
31630            break;
31631        }
31632        case command_FieldId_user: {
31633            retval = algo::Smallstr50_ReadStrptrMaybe(parent.user, strval);
31634            break;
31635        }
31636        case command_FieldId_shell: {
31637            retval = bool_ReadStrptrMaybe(parent.shell, strval);
31638            break;
31639        }
31640        case command_FieldId_rsync_put: {
31641            retval = bool_ReadStrptrMaybe(parent.rsync_put, strval);
31642            break;
31643        }
31644        case command_FieldId_rsync_get: {
31645            retval = bool_ReadStrptrMaybe(parent.rsync_get, strval);
31646            break;
31647        }
31648        case command_FieldId_rsync_opts: {
31649            retval = algo::cstring_ReadStrptrMaybe(parent.rsync_opts, strval);
31650            break;
31651        }
31652        case command_FieldId_local: {
31653            retval = algo::cstring_ReadStrptrMaybe(parent.local, strval);
31654            break;
31655        }
31656        case command_FieldId_remote: {
31657            retval = algo::cstring_ReadStrptrMaybe(parent.remote, strval);
31658            break;
31659        }
31660        case command_FieldId_dry_run: {
31661            retval = bool_ReadStrptrMaybe(parent.dry_run, strval);
31662            break;
31663        }
31664        case command_FieldId_maxtime: {
31665            retval = u32_ReadStrptrMaybe(parent.maxtime, strval);
31666            break;
31667        }
31668        case command_FieldId_cmd: {
31669            retval = algo::cstring_ReadStrptrMaybe(parent.cmd, strval);
31670            break;
31671        }
31672        case command_FieldId_q: {
31673            retval = bool_ReadStrptrMaybe(parent.q, strval);
31674            break;
31675        }
31676        default: break;
31677    }
31678    if (!retval) {
31679        algo_lib::AppendErrtext("attr",field);
31680    }
31681    return retval;
31682}
31683
31684// --- command.x2node..ReadTupleMaybe
31685// Read fields of command::x2node from attributes of ascii tuple TUPLE
31686bool command::x2node_ReadTupleMaybe(command::x2node &parent, algo::Tuple &tuple) {
31687    bool retval = true;
31688    int anon_idx = 0;
31689    ind_beg(algo::Tuple_attrs_curs,attr,tuple) {
31690        if (ch_N(attr.name) == 0) {
31691            attr.name = x2node_GetAnon(parent, anon_idx++);
31692        }
31693        retval = x2node_ReadFieldMaybe(parent, attr.name, attr.value);
31694        if (!retval) {
31695            break;
31696        }
31697    }ind_end;
31698    return retval;
31699}
31700
31701// --- command.x2node..Init
31702// Set all fields to initial values.
31703void command::x2node_Init(command::x2node& parent) {
31704    parent.in = algo::strptr("data");
31705    Regx_ReadSql(parent.envnode, "", true);
31706    parent.root = bool(false);
31707    parent.user = algo::strptr("");
31708    parent.shell = bool(false);
31709    parent.rsync_put = bool(false);
31710    parent.rsync_get = bool(false);
31711    parent.rsync_opts = algo::strptr("-ai");
31712    parent.local = algo::strptr("");
31713    parent.remote = algo::strptr("");
31714    parent.dry_run = bool(false);
31715    parent.maxtime = u32(0);
31716    parent.cmd = algo::strptr("");
31717    parent.q = bool(false);
31718}
31719
31720// --- command.x2node..ToCmdline
31721// Convenience function that returns a full command line
31722// Assume command is in a directory called bin
31723tempstr command::x2node_ToCmdline(command::x2node& row) {
31724    tempstr ret;
31725    ret << "bin/x2node ";
31726    x2node_PrintArgv(row, ret);
31727    // inherit less intense verbose, debug options
31728    for (int i = 1; i < algo_lib::_db.cmdline.verbose; i++) {
31729        ret << " -verbose";
31730    }
31731    for (int i = 1; i < algo_lib::_db.cmdline.debug; i++) {
31732        ret << " -debug";
31733    }
31734    return ret;
31735}
31736
31737// --- command.x2node..PrintArgv
31738// print string representation of ROW to string STR
31739// cfmt:command.x2node.Argv  printfmt:Tuple
31740void command::x2node_PrintArgv(command::x2node& row, algo::cstring& str) {
31741    algo::tempstr temp;
31742    (void)temp;
31743    (void)str;
31744    if (!(row.in == "data")) {
31745        ch_RemoveAll(temp);
31746        cstring_Print(row.in, temp);
31747        str << " -in:";
31748        strptr_PrintBash(temp,str);
31749    }
31750    ch_RemoveAll(temp);
31751    command::envnode_Print(const_cast<command::x2node&>(row), temp);
31752    str << " -envnode:";
31753    strptr_PrintBash(temp,str);
31754    if (!(row.root == false)) {
31755        ch_RemoveAll(temp);
31756        bool_Print(row.root, temp);
31757        str << " -root:";
31758        strptr_PrintBash(temp,str);
31759    }
31760    if (!(row.user == "")) {
31761        ch_RemoveAll(temp);
31762        Smallstr50_Print(row.user, temp);
31763        str << " -user:";
31764        strptr_PrintBash(temp,str);
31765    }
31766    if (!(row.shell == false)) {
31767        ch_RemoveAll(temp);
31768        bool_Print(row.shell, temp);
31769        str << " -shell:";
31770        strptr_PrintBash(temp,str);
31771    }
31772    if (!(row.rsync_put == false)) {
31773        ch_RemoveAll(temp);
31774        bool_Print(row.rsync_put, temp);
31775        str << " -rsync_put:";
31776        strptr_PrintBash(temp,str);
31777    }
31778    if (!(row.rsync_get == false)) {
31779        ch_RemoveAll(temp);
31780        bool_Print(row.rsync_get, temp);
31781        str << " -rsync_get:";
31782        strptr_PrintBash(temp,str);
31783    }
31784    if (!(row.rsync_opts == "-ai")) {
31785        ch_RemoveAll(temp);
31786        cstring_Print(row.rsync_opts, temp);
31787        str << " -rsync_opts:";
31788        strptr_PrintBash(temp,str);
31789    }
31790    if (!(row.local == "")) {
31791        ch_RemoveAll(temp);
31792        cstring_Print(row.local, temp);
31793        str << " -local:";
31794        strptr_PrintBash(temp,str);
31795    }
31796    if (!(row.remote == "")) {
31797        ch_RemoveAll(temp);
31798        cstring_Print(row.remote, temp);
31799        str << " -remote:";
31800        strptr_PrintBash(temp,str);
31801    }
31802    if (!(row.dry_run == false)) {
31803        ch_RemoveAll(temp);
31804        bool_Print(row.dry_run, temp);
31805        str << " -dry_run:";
31806        strptr_PrintBash(temp,str);
31807    }
31808    if (!(row.maxtime == 0)) {
31809        ch_RemoveAll(temp);
31810        u32_Print(row.maxtime, temp);
31811        str << " -maxtime:";
31812        strptr_PrintBash(temp,str);
31813    }
31814    ch_RemoveAll(temp);
31815    cstring_Print(row.cmd, temp);
31816    str << " -cmd:";
31817    strptr_PrintBash(temp,str);
31818    if (!(row.q == false)) {
31819        ch_RemoveAll(temp);
31820        bool_Print(row.q, temp);
31821        str << " -q:";
31822        strptr_PrintBash(temp,str);
31823    }
31824}
31825
31826// --- command.x2node..GetAnon
31827algo::strptr command::x2node_GetAnon(command::x2node &parent, i32 idx) {
31828    (void)parent;//only to avoid -Wunused-parameter
31829    switch(idx) {
31830        case(0): return strptr("envnode", 7);
31831        case(1): return strptr("cmd", 3);
31832        default: return algo::strptr();
31833    }
31834}
31835
31836// --- command.x2node..NArgs
31837// Used with command lines
31838// Return # of command-line arguments that must follow this argument
31839// If FIELD is invalid, return -1
31840i32 command::x2node_NArgs(command::FieldId field, algo::strptr& out_dflt, bool* out_anon) {
31841    i32 retval = 1;
31842    switch (field) {
31843        case command_FieldId_in: { // $comment
31844            *out_anon = false;
31845        } break;
31846        case command_FieldId_envnode: { // $comment
31847            *out_anon = true;
31848        } break;
31849        case command_FieldId_root: { // $comment
31850            *out_anon = false;
31851            retval=0;
31852            out_dflt="Y";
31853        } break;
31854        case command_FieldId_user: { // bool: no argument required but value may be specified as root:Y
31855            *out_anon = false;
31856        } break;
31857        case command_FieldId_shell: { // bool: no argument required but value may be specified as root:Y
31858            *out_anon = false;
31859            retval=0;
31860            out_dflt="Y";
31861        } break;
31862        case command_FieldId_rsync_put: { // bool: no argument required but value may be specified as shell:Y
31863            *out_anon = false;
31864            retval=0;
31865            out_dflt="Y";
31866        } break;
31867        case command_FieldId_rsync_get: { // bool: no argument required but value may be specified as rsync_put:Y
31868            *out_anon = false;
31869            retval=0;
31870            out_dflt="Y";
31871        } break;
31872        case command_FieldId_rsync_opts: { // bool: no argument required but value may be specified as rsync_get:Y
31873            *out_anon = false;
31874        } break;
31875        case command_FieldId_local: { // bool: no argument required but value may be specified as rsync_get:Y
31876            *out_anon = false;
31877        } break;
31878        case command_FieldId_remote: { // bool: no argument required but value may be specified as rsync_get:Y
31879            *out_anon = false;
31880        } break;
31881        case command_FieldId_dry_run: { // bool: no argument required but value may be specified as rsync_get:Y
31882            *out_anon = false;
31883            retval=0;
31884            out_dflt="Y";
31885        } break;
31886        case command_FieldId_maxtime: { // bool: no argument required but value may be specified as dry_run:Y
31887            *out_anon = false;
31888        } break;
31889        case command_FieldId_cmd: { // bool: no argument required but value may be specified as dry_run:Y
31890            *out_anon = true;
31891        } break;
31892        case command_FieldId_q: { // bool: no argument required but value may be specified as dry_run:Y
31893            *out_anon = false;
31894            retval=0;
31895            out_dflt="Y";
31896        } break;
31897        default:
31898        retval=-1; // unrecognized
31899    }
31900    return retval;
31901}
31902
31903// --- command.x2node_proc.x2node.Start
31904// Start subprocess
31905// If subprocess already running, do nothing. Otherwise, start it
31906int command::x2node_Start(command::x2node_proc& parent) {
31907    int retval = 0;
31908    if (parent.pid == 0) {
31909        verblog(x2node_ToCmdline(parent)); // maybe print command
31910#ifdef WIN32
31911        algo_lib::ResolveExecFname(parent.path);
31912        tempstr cmdline(x2node_ToCmdline(parent));
31913        parent.pid = dospawn(Zeroterm(parent.path),Zeroterm(cmdline),parent.timeout,parent.fstdin,parent.fstdout,parent.fstderr);
31914#else
31915        parent.pid = fork();
31916        if (parent.pid == 0) { // child
31917            algo_lib::DieWithParent();
31918            if (parent.timeout > 0) {
31919                alarm(parent.timeout);
31920            }
31921            if (retval==0) retval=algo_lib::ApplyRedirect(parent.fstdin , 0);
31922            if (retval==0) retval=algo_lib::ApplyRedirect(parent.fstdout, 1);
31923            if (retval==0) retval=algo_lib::ApplyRedirect(parent.fstderr, 2);
31924            if (retval==0) retval= x2node_Execv(parent);
31925            if (retval != 0) { // if start fails, print error
31926                int err=errno;
31927                prerr("command.x2node_execv"
31928                <<Keyval("errno",err)
31929                <<Keyval("errstr",strerror(err))
31930                <<Keyval("comment","Execv failed"));
31931            }
31932            _exit(127); // if failed to start, exit anyway
31933        } else if (parent.pid == -1) {
31934            retval = errno; // failed to fork
31935        }
31936#endif
31937    }
31938    parent.status = parent.pid > 0 ? 0 : -1; // if didn't start, set error status
31939    return retval;
31940}
31941
31942// --- command.x2node_proc.x2node.StartRead
31943// Start subprocess & Read output
31944algo::Fildes command::x2node_StartRead(command::x2node_proc& parent, algo_lib::FFildes &read) {
31945    int pipefd[2];
31946    int rc=pipe(pipefd);
31947    (void)rc;
31948    read.fd.value = pipefd[0];
31949    parent.fstdout  << ">&" << pipefd[1];
31950    x2node_Start(parent);
31951    (void)close(pipefd[1]);
31952    return read.fd;
31953}
31954
31955// --- command.x2node_proc.x2node.Kill
31956// Kill subprocess and wait
31957void command::x2node_Kill(command::x2node_proc& parent) {
31958    if (parent.pid != 0) {
31959        kill(parent.pid,9);
31960        x2node_Wait(parent);
31961    }
31962}
31963
31964// --- command.x2node_proc.x2node.Wait
31965// Wait for subprocess to return
31966void command::x2node_Wait(command::x2node_proc& parent) {
31967    if (parent.pid > 0) {
31968        int wait_flags = 0;
31969        int wait_status = 0;
31970        int rc = -1;
31971        do {
31972            // really wait for subprocess to exit
31973            rc = waitpid(parent.pid,&wait_status,wait_flags);
31974        } while (rc==-1 && errno==EINTR);
31975        if (rc == parent.pid) {
31976            parent.status = wait_status;
31977            parent.pid = 0;
31978        }
31979    }
31980}
31981
31982// --- command.x2node_proc.x2node.Exec
31983// Start + Wait
31984// Execute subprocess and return exit code
31985int command::x2node_Exec(command::x2node_proc& parent) {
31986    x2node_Start(parent);
31987    x2node_Wait(parent);
31988    return parent.status;
31989}
31990
31991// --- command.x2node_proc.x2node.ExecX
31992// Start + Wait, throw exception on error
31993// Execute subprocess; throw human-readable exception on error
31994void command::x2node_ExecX(command::x2node_proc& parent) {
31995    int rc = x2node_Exec(parent);
31996    vrfy(rc==0, tempstr() << "algo_lib.exec" << Keyval("cmd",x2node_ToCmdline(parent))
31997    << Keyval("comment",algo::DescribeWaitStatus(parent.status)));
31998}
31999
32000// --- command.x2node_proc.x2node.Execv
32001// Call execv()
32002// Call execv with specified parameters
32003int command::x2node_Execv(command::x2node_proc& parent) {
32004    int ret = 0;
32005    algo::StringAry args;
32006    x2node_ToArgv(parent, args);
32007    char **argv = (char**)alloca((ary_N(args)+1)*sizeof(*argv));
32008    ind_beg(algo::StringAry_ary_curs,arg,args) {
32009        argv[ind_curs(arg).index] = Zeroterm(arg);
32010    }ind_end;
32011    argv[ary_N(args)] = NULL;
32012    // if parent.path is relative, search for it in PATH
32013    algo_lib::ResolveExecFname(parent.path);
32014    ret = execv(Zeroterm(parent.path),argv);
32015    return ret;
32016}
32017
32018// --- command.x2node_proc.x2node.ToCmdline
32019algo::tempstr command::x2node_ToCmdline(command::x2node_proc& parent) {
32020    algo::tempstr retval;
32021    retval << parent.path << " ";
32022    command::x2node_PrintArgv(parent.cmd,retval);
32023    if (ch_N(parent.fstdin)) {
32024        retval << " " << parent.fstdin;
32025    }
32026    if (ch_N(parent.fstdout)) {
32027        retval << " " << parent.fstdout;
32028    }
32029    if (ch_N(parent.fstderr)) {
32030        retval << " 2" << parent.fstderr;
32031    }
32032    return retval;
32033}
32034
32035// --- command.x2node_proc.x2node.ToArgv
32036// Form array from the command line
32037void command::x2node_ToArgv(command::x2node_proc& parent, algo::StringAry& args) {
32038    ary_RemoveAll(args);
32039    ary_Alloc(args) << parent.path;
32040
32041    if (parent.cmd.in != "data") {
32042        cstring *arg = &ary_Alloc(args);
32043        *arg << "-in:";
32044        cstring_Print(parent.cmd.in, *arg);
32045    }
32046
32047    if (parent.cmd.envnode.expr != "") {
32048        cstring *arg = &ary_Alloc(args);
32049        *arg << "-envnode:";
32050        command::envnode_Print(parent.cmd, *arg);
32051    }
32052
32053    if (parent.cmd.root != false) {
32054        cstring *arg = &ary_Alloc(args);
32055        *arg << "-root:";
32056        bool_Print(parent.cmd.root, *arg);
32057    }
32058
32059    if (parent.cmd.user != "") {
32060        cstring *arg = &ary_Alloc(args);
32061        *arg << "-user:";
32062        Smallstr50_Print(parent.cmd.user, *arg);
32063    }
32064
32065    if (parent.cmd.shell != false) {
32066        cstring *arg = &ary_Alloc(args);
32067        *arg << "-shell:";
32068        bool_Print(parent.cmd.shell, *arg);
32069    }
32070
32071    if (parent.cmd.rsync_put != false) {
32072        cstring *arg = &ary_Alloc(args);
32073        *arg << "-rsync_put:";
32074        bool_Print(parent.cmd.rsync_put, *arg);
32075    }
32076
32077    if (parent.cmd.rsync_get != false) {
32078        cstring *arg = &ary_Alloc(args);
32079        *arg << "-rsync_get:";
32080        bool_Print(parent.cmd.rsync_get, *arg);
32081    }
32082
32083    if (parent.cmd.rsync_opts != "-ai") {
32084        cstring *arg = &ary_Alloc(args);
32085        *arg << "-rsync_opts:";
32086        cstring_Print(parent.cmd.rsync_opts, *arg);
32087    }
32088
32089    if (parent.cmd.local != "") {
32090        cstring *arg = &ary_Alloc(args);
32091        *arg << "-local:";
32092        cstring_Print(parent.cmd.local, *arg);
32093    }
32094
32095    if (parent.cmd.remote != "") {
32096        cstring *arg = &ary_Alloc(args);
32097        *arg << "-remote:";
32098        cstring_Print(parent.cmd.remote, *arg);
32099    }
32100
32101    if (parent.cmd.dry_run != false) {
32102        cstring *arg = &ary_Alloc(args);
32103        *arg << "-dry_run:";
32104        bool_Print(parent.cmd.dry_run, *arg);
32105    }
32106
32107    if (parent.cmd.maxtime != 0) {
32108        cstring *arg = &ary_Alloc(args);
32109        *arg << "-maxtime:";
32110        u32_Print(parent.cmd.maxtime, *arg);
32111    }
32112
32113    if (parent.cmd.cmd != "") {
32114        cstring *arg = &ary_Alloc(args);
32115        *arg << "-cmd:";
32116        cstring_Print(parent.cmd.cmd, *arg);
32117    }
32118
32119    if (parent.cmd.q != false) {
32120        cstring *arg = &ary_Alloc(args);
32121        *arg << "-q:";
32122        bool_Print(parent.cmd.q, *arg);
32123    }
32124    for (int i=1; i < algo_lib::_db.cmdline.verbose; ++i) {
32125        ary_Alloc(args) << "-verbose";
32126    }
32127}
32128
32129// --- command.x2node_proc..Uninit
32130void command::x2node_proc_Uninit(command::x2node_proc& parent) {
32131    command::x2node_proc &row = parent; (void)row;
32132
32133    // command.x2node_proc.x2node.Uninit (Exec)  //
32134    x2node_Kill(parent); // kill child, ensure forward progress
32135}
32136
32137// --- command.x2read..ReadFieldMaybe
32138bool command::x2read_ReadFieldMaybe(command::x2read& parent, algo::strptr field, algo::strptr strval) {
32139    bool retval = true;
32140    command::FieldId field_id;
32141    (void)value_SetStrptrMaybe(field_id,field);
32142    switch(field_id) {
32143        case command_FieldId_in: {
32144            retval = algo::cstring_ReadStrptrMaybe(parent.in, strval);
32145            break;
32146        }
32147        case command_FieldId_gw: {
32148            retval = ietf::Ipport_ReadStrptrMaybe(parent.gw, strval);
32149            break;
32150        }
32151        default: break;
32152    }
32153    if (!retval) {
32154        algo_lib::AppendErrtext("attr",field);
32155    }
32156    return retval;
32157}
32158
32159// --- command.x2read..ReadTupleMaybe
32160// Read fields of command::x2read from attributes of ascii tuple TUPLE
32161bool command::x2read_ReadTupleMaybe(command::x2read &parent, algo::Tuple &tuple) {
32162    bool retval = true;
32163    ind_beg(algo::Tuple_attrs_curs,attr,tuple) {
32164        retval = x2read_ReadFieldMaybe(parent, attr.name, attr.value);
32165        if (!retval) {
32166            break;
32167        }
32168    }ind_end;
32169    return retval;
32170}
32171
32172// --- command.x2read..ToCmdline
32173// Convenience function that returns a full command line
32174// Assume command is in a directory called bin
32175tempstr command::x2read_ToCmdline(command::x2read& row) {
32176    tempstr ret;
32177    ret << "bin/x2read ";
32178    x2read_PrintArgv(row, ret);
32179    // inherit less intense verbose, debug options
32180    for (int i = 1; i < algo_lib::_db.cmdline.verbose; i++) {
32181        ret << " -verbose";
32182    }
32183    for (int i = 1; i < algo_lib::_db.cmdline.debug; i++) {
32184        ret << " -debug";
32185    }
32186    return ret;
32187}
32188
32189// --- command.x2read..PrintArgv
32190// print string representation of ROW to string STR
32191// cfmt:command.x2read.Argv  printfmt:Tuple
32192void command::x2read_PrintArgv(command::x2read& row, algo::cstring& str) {
32193    algo::tempstr temp;
32194    (void)temp;
32195    (void)str;
32196    if (!(row.in == "data")) {
32197        ch_RemoveAll(temp);
32198        cstring_Print(row.in, temp);
32199        str << " -in:";
32200        strptr_PrintBash(temp,str);
32201    }
32202    if (!(Ipport_Eq(row.gw, ietf::Ipport()))) {
32203        ch_RemoveAll(temp);
32204        Ipport_Print(row.gw, temp);
32205        str << " -gw:";
32206        strptr_PrintBash(temp,str);
32207    }
32208}
32209
32210// --- command.x2read..NArgs
32211// Used with command lines
32212// Return # of command-line arguments that must follow this argument
32213// If FIELD is invalid, return -1
32214i32 command::x2read_NArgs(command::FieldId field, algo::strptr& out_dflt, bool* out_anon) {
32215    i32 retval = 1;
32216    switch (field) {
32217        case command_FieldId_in: { // $comment
32218            *out_anon = false;
32219        } break;
32220        case command_FieldId_gw: { // $comment
32221            *out_anon = false;
32222        } break;
32223        default:
32224        retval=-1; // unrecognized
32225    }
32226    (void)out_dflt;//only to avoid -Wunused-parameter
32227    return retval;
32228}
32229
32230// --- command.x2read_proc.x2read.Start
32231// Start subprocess
32232// If subprocess already running, do nothing. Otherwise, start it
32233int command::x2read_Start(command::x2read_proc& parent) {
32234    int retval = 0;
32235    if (parent.pid == 0) {
32236        verblog(x2read_ToCmdline(parent)); // maybe print command
32237#ifdef WIN32
32238        algo_lib::ResolveExecFname(parent.path);
32239        tempstr cmdline(x2read_ToCmdline(parent));
32240        parent.pid = dospawn(Zeroterm(parent.path),Zeroterm(cmdline),parent.timeout,parent.fstdin,parent.fstdout,parent.fstderr);
32241#else
32242        parent.pid = fork();
32243        if (parent.pid == 0) { // child
32244            algo_lib::DieWithParent();
32245            if (parent.timeout > 0) {
32246                alarm(parent.timeout);
32247            }
32248            if (retval==0) retval=algo_lib::ApplyRedirect(parent.fstdin , 0);
32249            if (retval==0) retval=algo_lib::ApplyRedirect(parent.fstdout, 1);
32250            if (retval==0) retval=algo_lib::ApplyRedirect(parent.fstderr, 2);
32251            if (retval==0) retval= x2read_Execv(parent);
32252            if (retval != 0) { // if start fails, print error
32253                int err=errno;
32254                prerr("command.x2read_execv"
32255                <<Keyval("errno",err)
32256                <<Keyval("errstr",strerror(err))
32257                <<Keyval("comment","Execv failed"));
32258            }
32259            _exit(127); // if failed to start, exit anyway
32260        } else if (parent.pid == -1) {
32261            retval = errno; // failed to fork
32262        }
32263#endif
32264    }
32265    parent.status = parent.pid > 0 ? 0 : -1; // if didn't start, set error status
32266    return retval;
32267}
32268
32269// --- command.x2read_proc.x2read.StartRead
32270// Start subprocess & Read output
32271algo::Fildes command::x2read_StartRead(command::x2read_proc& parent, algo_lib::FFildes &read) {
32272    int pipefd[2];
32273    int rc=pipe(pipefd);
32274    (void)rc;
32275    read.fd.value = pipefd[0];
32276    parent.fstdout  << ">&" << pipefd[1];
32277    x2read_Start(parent);
32278    (void)close(pipefd[1]);
32279    return read.fd;
32280}
32281
32282// --- command.x2read_proc.x2read.Kill
32283// Kill subprocess and wait
32284void command::x2read_Kill(command::x2read_proc& parent) {
32285    if (parent.pid != 0) {
32286        kill(parent.pid,9);
32287        x2read_Wait(parent);
32288    }
32289}
32290
32291// --- command.x2read_proc.x2read.Wait
32292// Wait for subprocess to return
32293void command::x2read_Wait(command::x2read_proc& parent) {
32294    if (parent.pid > 0) {
32295        int wait_flags = 0;
32296        int wait_status = 0;
32297        int rc = -1;
32298        do {
32299            // really wait for subprocess to exit
32300            rc = waitpid(parent.pid,&wait_status,wait_flags);
32301        } while (rc==-1 && errno==EINTR);
32302        if (rc == parent.pid) {
32303            parent.status = wait_status;
32304            parent.pid = 0;
32305        }
32306    }
32307}
32308
32309// --- command.x2read_proc.x2read.Exec
32310// Start + Wait
32311// Execute subprocess and return exit code
32312int command::x2read_Exec(command::x2read_proc& parent) {
32313    x2read_Start(parent);
32314    x2read_Wait(parent);
32315    return parent.status;
32316}
32317
32318// --- command.x2read_proc.x2read.ExecX
32319// Start + Wait, throw exception on error
32320// Execute subprocess; throw human-readable exception on error
32321void command::x2read_ExecX(command::x2read_proc& parent) {
32322    int rc = x2read_Exec(parent);
32323    vrfy(rc==0, tempstr() << "algo_lib.exec" << Keyval("cmd",x2read_ToCmdline(parent))
32324    << Keyval("comment",algo::DescribeWaitStatus(parent.status)));
32325}
32326
32327// --- command.x2read_proc.x2read.Execv
32328// Call execv()
32329// Call execv with specified parameters
32330int command::x2read_Execv(command::x2read_proc& parent) {
32331    int ret = 0;
32332    algo::StringAry args;
32333    x2read_ToArgv(parent, args);
32334    char **argv = (char**)alloca((ary_N(args)+1)*sizeof(*argv));
32335    ind_beg(algo::StringAry_ary_curs,arg,args) {
32336        argv[ind_curs(arg).index] = Zeroterm(arg);
32337    }ind_end;
32338    argv[ary_N(args)] = NULL;
32339    // if parent.path is relative, search for it in PATH
32340    algo_lib::ResolveExecFname(parent.path);
32341    ret = execv(Zeroterm(parent.path),argv);
32342    return ret;
32343}
32344
32345// --- command.x2read_proc.x2read.ToCmdline
32346algo::tempstr command::x2read_ToCmdline(command::x2read_proc& parent) {
32347    algo::tempstr retval;
32348    retval << parent.path << " ";
32349    command::x2read_PrintArgv(parent.cmd,retval);
32350    if (ch_N(parent.fstdin)) {
32351        retval << " " << parent.fstdin;
32352    }
32353    if (ch_N(parent.fstdout)) {
32354        retval << " " << parent.fstdout;
32355    }
32356    if (ch_N(parent.fstderr)) {
32357        retval << " 2" << parent.fstderr;
32358    }
32359    return retval;
32360}
32361
32362// --- command.x2read_proc.x2read.ToArgv
32363// Form array from the command line
32364void command::x2read_ToArgv(command::x2read_proc& parent, algo::StringAry& args) {
32365    ary_RemoveAll(args);
32366    ary_Alloc(args) << parent.path;
32367
32368    if (parent.cmd.in != "data") {
32369        cstring *arg = &ary_Alloc(args);
32370        *arg << "-in:";
32371        cstring_Print(parent.cmd.in, *arg);
32372    }
32373
32374    if (true) {
32375        cstring *arg = &ary_Alloc(args);
32376        *arg << "-gw:";
32377        Ipport_Print(parent.cmd.gw, *arg);
32378    }
32379    for (int i=1; i < algo_lib::_db.cmdline.verbose; ++i) {
32380        ary_Alloc(args) << "-verbose";
32381    }
32382}
32383
32384// --- command.x2read_proc..Uninit
32385void command::x2read_proc_Uninit(command::x2read_proc& parent) {
32386    command::x2read_proc &row = parent; (void)row;
32387
32388    // command.x2read_proc.x2read.Uninit (Exec)  //
32389    x2read_Kill(parent); // kill child, ensure forward progress
32390}
32391
32392// --- command.x2sup..ReadFieldMaybe
32393bool command::x2sup_ReadFieldMaybe(command::x2sup& parent, algo::strptr field, algo::strptr strval) {
32394    bool retval = true;
32395    command::FieldId field_id;
32396    (void)value_SetStrptrMaybe(field_id,field);
32397    switch(field_id) {
32398        case command_FieldId_in: {
32399            retval = algo::cstring_ReadStrptrMaybe(parent.in, strval);
32400            break;
32401        }
32402        case command_FieldId_i: {
32403            retval = bool_ReadStrptrMaybe(parent.i, strval);
32404            break;
32405        }
32406        default: break;
32407    }
32408    if (!retval) {
32409        algo_lib::AppendErrtext("attr",field);
32410    }
32411    return retval;
32412}
32413
32414// --- command.x2sup..ReadTupleMaybe
32415// Read fields of command::x2sup from attributes of ascii tuple TUPLE
32416bool command::x2sup_ReadTupleMaybe(command::x2sup &parent, algo::Tuple &tuple) {
32417    bool retval = true;
32418    ind_beg(algo::Tuple_attrs_curs,attr,tuple) {
32419        retval = x2sup_ReadFieldMaybe(parent, attr.name, attr.value);
32420        if (!retval) {
32421            break;
32422        }
32423    }ind_end;
32424    return retval;
32425}
32426
32427// --- command.x2sup..ToCmdline
32428// Convenience function that returns a full command line
32429// Assume command is in a directory called bin
32430tempstr command::x2sup_ToCmdline(command::x2sup& row) {
32431    tempstr ret;
32432    ret << "bin/x2sup ";
32433    x2sup_PrintArgv(row, ret);
32434    // inherit less intense verbose, debug options
32435    for (int i = 1; i < algo_lib::_db.cmdline.verbose; i++) {
32436        ret << " -verbose";
32437    }
32438    for (int i = 1; i < algo_lib::_db.cmdline.debug; i++) {
32439        ret << " -debug";
32440    }
32441    return ret;
32442}
32443
32444// --- command.x2sup..PrintArgv
32445// print string representation of ROW to string STR
32446// cfmt:command.x2sup.Argv  printfmt:Tuple
32447void command::x2sup_PrintArgv(command::x2sup& row, algo::cstring& str) {
32448    algo::tempstr temp;
32449    (void)temp;
32450    (void)str;
32451    if (!(row.in == "data")) {
32452        ch_RemoveAll(temp);
32453        cstring_Print(row.in, temp);
32454        str << " -in:";
32455        strptr_PrintBash(temp,str);
32456    }
32457    if (!(row.i == false)) {
32458        ch_RemoveAll(temp);
32459        bool_Print(row.i, temp);
32460        str << " -i:";
32461        strptr_PrintBash(temp,str);
32462    }
32463}
32464
32465// --- command.x2sup..NArgs
32466// Used with command lines
32467// Return # of command-line arguments that must follow this argument
32468// If FIELD is invalid, return -1
32469i32 command::x2sup_NArgs(command::FieldId field, algo::strptr& out_dflt, bool* out_anon) {
32470    i32 retval = 1;
32471    switch (field) {
32472        case command_FieldId_in: { // $comment
32473            *out_anon = false;
32474        } break;
32475        case command_FieldId_i: { // $comment
32476            *out_anon = false;
32477            retval=0;
32478            out_dflt="Y";
32479        } break;
32480        default:
32481        retval=-1; // unrecognized
32482    }
32483    return retval;
32484}
32485
32486// --- command.x2sup_proc.x2sup.Start
32487// Start subprocess
32488// If subprocess already running, do nothing. Otherwise, start it
32489int command::x2sup_Start(command::x2sup_proc& parent) {
32490    int retval = 0;
32491    if (parent.pid == 0) {
32492        verblog(x2sup_ToCmdline(parent)); // maybe print command
32493#ifdef WIN32
32494        algo_lib::ResolveExecFname(parent.path);
32495        tempstr cmdline(x2sup_ToCmdline(parent));
32496        parent.pid = dospawn(Zeroterm(parent.path),Zeroterm(cmdline),parent.timeout,parent.fstdin,parent.fstdout,parent.fstderr);
32497#else
32498        parent.pid = fork();
32499        if (parent.pid == 0) { // child
32500            algo_lib::DieWithParent();
32501            if (parent.timeout > 0) {
32502                alarm(parent.timeout);
32503            }
32504            if (retval==0) retval=algo_lib::ApplyRedirect(parent.fstdin , 0);
32505            if (retval==0) retval=algo_lib::ApplyRedirect(parent.fstdout, 1);
32506            if (retval==0) retval=algo_lib::ApplyRedirect(parent.fstderr, 2);
32507            if (retval==0) retval= x2sup_Execv(parent);
32508            if (retval != 0) { // if start fails, print error
32509                int err=errno;
32510                prerr("command.x2sup_execv"
32511                <<Keyval("errno",err)
32512                <<Keyval("errstr",strerror(err))
32513                <<Keyval("comment","Execv failed"));
32514            }
32515            _exit(127); // if failed to start, exit anyway
32516        } else if (parent.pid == -1) {
32517            retval = errno; // failed to fork
32518        }
32519#endif
32520    }
32521    parent.status = parent.pid > 0 ? 0 : -1; // if didn't start, set error status
32522    return retval;
32523}
32524
32525// --- command.x2sup_proc.x2sup.StartRead
32526// Start subprocess & Read output
32527algo::Fildes command::x2sup_StartRead(command::x2sup_proc& parent, algo_lib::FFildes &read) {
32528    int pipefd[2];
32529    int rc=pipe(pipefd);
32530    (void)rc;
32531    read.fd.value = pipefd[0];
32532    parent.fstdout  << ">&" << pipefd[1];
32533    x2sup_Start(parent);
32534    (void)close(pipefd[1]);
32535    return read.fd;
32536}
32537
32538// --- command.x2sup_proc.x2sup.Kill
32539// Kill subprocess and wait
32540void command::x2sup_Kill(command::x2sup_proc& parent) {
32541    if (parent.pid != 0) {
32542        kill(parent.pid,9);
32543        x2sup_Wait(parent);
32544    }
32545}
32546
32547// --- command.x2sup_proc.x2sup.Wait
32548// Wait for subprocess to return
32549void command::x2sup_Wait(command::x2sup_proc& parent) {
32550    if (parent.pid > 0) {
32551        int wait_flags = 0;
32552        int wait_status = 0;
32553        int rc = -1;
32554        do {
32555            // really wait for subprocess to exit
32556            rc = waitpid(parent.pid,&wait_status,wait_flags);
32557        } while (rc==-1 && errno==EINTR);
32558        if (rc == parent.pid) {
32559            parent.status = wait_status;
32560            parent.pid = 0;
32561        }
32562    }
32563}
32564
32565// --- command.x2sup_proc.x2sup.Exec
32566// Start + Wait
32567// Execute subprocess and return exit code
32568int command::x2sup_Exec(command::x2sup_proc& parent) {
32569    x2sup_Start(parent);
32570    x2sup_Wait(parent);
32571    return parent.status;
32572}
32573
32574// --- command.x2sup_proc.x2sup.ExecX
32575// Start + Wait, throw exception on error
32576// Execute subprocess; throw human-readable exception on error
32577void command::x2sup_ExecX(command::x2sup_proc& parent) {
32578    int rc = x2sup_Exec(parent);
32579    vrfy(rc==0, tempstr() << "algo_lib.exec" << Keyval("cmd",x2sup_ToCmdline(parent))
32580    << Keyval("comment",algo::DescribeWaitStatus(parent.status)));
32581}
32582
32583// --- command.x2sup_proc.x2sup.Execv
32584// Call execv()
32585// Call execv with specified parameters
32586int command::x2sup_Execv(command::x2sup_proc& parent) {
32587    int ret = 0;
32588    algo::StringAry args;
32589    x2sup_ToArgv(parent, args);
32590    char **argv = (char**)alloca((ary_N(args)+1)*sizeof(*argv));
32591    ind_beg(algo::StringAry_ary_curs,arg,args) {
32592        argv[ind_curs(arg).index] = Zeroterm(arg);
32593    }ind_end;
32594    argv[ary_N(args)] = NULL;
32595    // if parent.path is relative, search for it in PATH
32596    algo_lib::ResolveExecFname(parent.path);
32597    ret = execv(Zeroterm(parent.path),argv);
32598    return ret;
32599}
32600
32601// --- command.x2sup_proc.x2sup.ToCmdline
32602algo::tempstr command::x2sup_ToCmdline(command::x2sup_proc& parent) {
32603    algo::tempstr retval;
32604    retval << parent.path << " ";
32605    command::x2sup_PrintArgv(parent.cmd,retval);
32606    if (ch_N(parent.fstdin)) {
32607        retval << " " << parent.fstdin;
32608    }
32609    if (ch_N(parent.fstdout)) {
32610        retval << " " << parent.fstdout;
32611    }
32612    if (ch_N(parent.fstderr)) {
32613        retval << " 2" << parent.fstderr;
32614    }
32615    return retval;
32616}
32617
32618// --- command.x2sup_proc.x2sup.ToArgv
32619// Form array from the command line
32620void command::x2sup_ToArgv(command::x2sup_proc& parent, algo::StringAry& args) {
32621    ary_RemoveAll(args);
32622    ary_Alloc(args) << parent.path;
32623
32624    if (parent.cmd.in != "data") {
32625        cstring *arg = &ary_Alloc(args);
32626        *arg << "-in:";
32627        cstring_Print(parent.cmd.in, *arg);
32628    }
32629
32630    if (parent.cmd.i != false) {
32631        cstring *arg = &ary_Alloc(args);
32632        *arg << "-i:";
32633        bool_Print(parent.cmd.i, *arg);
32634    }
32635    for (int i=1; i < algo_lib::_db.cmdline.verbose; ++i) {
32636        ary_Alloc(args) << "-verbose";
32637    }
32638}
32639
32640// --- command.x2sup_proc..Uninit
32641void command::x2sup_proc_Uninit(command::x2sup_proc& parent) {
32642    command::x2sup_proc &row = parent; (void)row;
32643
32644    // command.x2sup_proc.x2sup.Uninit (Exec)  //
32645    x2sup_Kill(parent); // kill child, ensure forward progress
32646}
32647
32648// --- command.x2txn..ReadFieldMaybe
32649bool command::x2txn_ReadFieldMaybe(command::x2txn& parent, algo::strptr field, algo::strptr strval) {
32650    bool retval = true;
32651    command::FieldId field_id;
32652    (void)value_SetStrptrMaybe(field_id,field);
32653    switch(field_id) {
32654        case command_FieldId_in: {
32655            retval = algo::cstring_ReadStrptrMaybe(parent.in, strval);
32656            break;
32657        }
32658        case command_FieldId_prefix: {
32659            retval = algo::cstring_ReadStrptrMaybe(parent.prefix, strval);
32660            break;
32661        }
32662        default: break;
32663    }
32664    if (!retval) {
32665        algo_lib::AppendErrtext("attr",field);
32666    }
32667    return retval;
32668}
32669
32670// --- command.x2txn..ReadTupleMaybe
32671// Read fields of command::x2txn from attributes of ascii tuple TUPLE
32672bool command::x2txn_ReadTupleMaybe(command::x2txn &parent, algo::Tuple &tuple) {
32673    bool retval = true;
32674    ind_beg(algo::Tuple_attrs_curs,attr,tuple) {
32675        retval = x2txn_ReadFieldMaybe(parent, attr.name, attr.value);
32676        if (!retval) {
32677            break;
32678        }
32679    }ind_end;
32680    return retval;
32681}
32682
32683// --- command.x2txn..ToCmdline
32684// Convenience function that returns a full command line
32685// Assume command is in a directory called bin
32686tempstr command::x2txn_ToCmdline(command::x2txn& row) {
32687    tempstr ret;
32688    ret << "bin/x2txn ";
32689    x2txn_PrintArgv(row, ret);
32690    // inherit less intense verbose, debug options
32691    for (int i = 1; i < algo_lib::_db.cmdline.verbose; i++) {
32692        ret << " -verbose";
32693    }
32694    for (int i = 1; i < algo_lib::_db.cmdline.debug; i++) {
32695        ret << " -debug";
32696    }
32697    return ret;
32698}
32699
32700// --- command.x2txn..PrintArgv
32701// print string representation of ROW to string STR
32702// cfmt:command.x2txn.Argv  printfmt:Tuple
32703void command::x2txn_PrintArgv(command::x2txn& row, algo::cstring& str) {
32704    algo::tempstr temp;
32705    (void)temp;
32706    (void)str;
32707    if (!(row.in == "data")) {
32708        ch_RemoveAll(temp);
32709        cstring_Print(row.in, temp);
32710        str << " -in:";
32711        strptr_PrintBash(temp,str);
32712    }
32713    if (!(row.prefix == "")) {
32714        ch_RemoveAll(temp);
32715        cstring_Print(row.prefix, temp);
32716        str << " -prefix:";
32717        strptr_PrintBash(temp,str);
32718    }
32719}
32720
32721// --- command.x2txn..NArgs
32722// Used with command lines
32723// Return # of command-line arguments that must follow this argument
32724// If FIELD is invalid, return -1
32725i32 command::x2txn_NArgs(command::FieldId field, algo::strptr& out_dflt, bool* out_anon) {
32726    i32 retval = 1;
32727    switch (field) {
32728        case command_FieldId_in: { // $comment
32729            *out_anon = false;
32730        } break;
32731        case command_FieldId_prefix: { // $comment
32732            *out_anon = false;
32733        } break;
32734        default:
32735        retval=-1; // unrecognized
32736    }
32737    (void)out_dflt;//only to avoid -Wunused-parameter
32738    return retval;
32739}
32740
32741// --- command.x2txn_proc.x2txn.Start
32742// Start subprocess
32743// If subprocess already running, do nothing. Otherwise, start it
32744int command::x2txn_Start(command::x2txn_proc& parent) {
32745    int retval = 0;
32746    if (parent.pid == 0) {
32747        verblog(x2txn_ToCmdline(parent)); // maybe print command
32748#ifdef WIN32
32749        algo_lib::ResolveExecFname(parent.path);
32750        tempstr cmdline(x2txn_ToCmdline(parent));
32751        parent.pid = dospawn(Zeroterm(parent.path),Zeroterm(cmdline),parent.timeout,parent.fstdin,parent.fstdout,parent.fstderr);
32752#else
32753        parent.pid = fork();
32754        if (parent.pid == 0) { // child
32755            algo_lib::DieWithParent();
32756            if (parent.timeout > 0) {
32757                alarm(parent.timeout);
32758            }
32759            if (retval==0) retval=algo_lib::ApplyRedirect(parent.fstdin , 0);
32760            if (retval==0) retval=algo_lib::ApplyRedirect(parent.fstdout, 1);
32761            if (retval==0) retval=algo_lib::ApplyRedirect(parent.fstderr, 2);
32762            if (retval==0) retval= x2txn_Execv(parent);
32763            if (retval != 0) { // if start fails, print error
32764                int err=errno;
32765                prerr("command.x2txn_execv"
32766                <<Keyval("errno",err)
32767                <<Keyval("errstr",strerror(err))
32768                <<Keyval("comment","Execv failed"));
32769            }
32770            _exit(127); // if failed to start, exit anyway
32771        } else if (parent.pid == -1) {
32772            retval = errno; // failed to fork
32773        }
32774#endif
32775    }
32776    parent.status = parent.pid > 0 ? 0 : -1; // if didn't start, set error status
32777    return retval;
32778}
32779
32780// --- command.x2txn_proc.x2txn.StartRead
32781// Start subprocess & Read output
32782algo::Fildes command::x2txn_StartRead(command::x2txn_proc& parent, algo_lib::FFildes &read) {
32783    int pipefd[2];
32784    int rc=pipe(pipefd);
32785    (void)rc;
32786    read.fd.value = pipefd[0];
32787    parent.fstdout  << ">&" << pipefd[1];
32788    x2txn_Start(parent);
32789    (void)close(pipefd[1]);
32790    return read.fd;
32791}
32792
32793// --- command.x2txn_proc.x2txn.Kill
32794// Kill subprocess and wait
32795void command::x2txn_Kill(command::x2txn_proc& parent) {
32796    if (parent.pid != 0) {
32797        kill(parent.pid,9);
32798        x2txn_Wait(parent);
32799    }
32800}
32801
32802// --- command.x2txn_proc.x2txn.Wait
32803// Wait for subprocess to return
32804void command::x2txn_Wait(command::x2txn_proc& parent) {
32805    if (parent.pid > 0) {
32806        int wait_flags = 0;
32807        int wait_status = 0;
32808        int rc = -1;
32809        do {
32810            // really wait for subprocess to exit
32811            rc = waitpid(parent.pid,&wait_status,wait_flags);
32812        } while (rc==-1 && errno==EINTR);
32813        if (rc == parent.pid) {
32814            parent.status = wait_status;
32815            parent.pid = 0;
32816        }
32817    }
32818}
32819
32820// --- command.x2txn_proc.x2txn.Exec
32821// Start + Wait
32822// Execute subprocess and return exit code
32823int command::x2txn_Exec(command::x2txn_proc& parent) {
32824    x2txn_Start(parent);
32825    x2txn_Wait(parent);
32826    return parent.status;
32827}
32828
32829// --- command.x2txn_proc.x2txn.ExecX
32830// Start + Wait, throw exception on error
32831// Execute subprocess; throw human-readable exception on error
32832void command::x2txn_ExecX(command::x2txn_proc& parent) {
32833    int rc = x2txn_Exec(parent);
32834    vrfy(rc==0, tempstr() << "algo_lib.exec" << Keyval("cmd",x2txn_ToCmdline(parent))
32835    << Keyval("comment",algo::DescribeWaitStatus(parent.status)));
32836}
32837
32838// --- command.x2txn_proc.x2txn.Execv
32839// Call execv()
32840// Call execv with specified parameters
32841int command::x2txn_Execv(command::x2txn_proc& parent) {
32842    int ret = 0;
32843    algo::StringAry args;
32844    x2txn_ToArgv(parent, args);
32845    char **argv = (char**)alloca((ary_N(args)+1)*sizeof(*argv));
32846    ind_beg(algo::StringAry_ary_curs,arg,args) {
32847        argv[ind_curs(arg).index] = Zeroterm(arg);
32848    }ind_end;
32849    argv[ary_N(args)] = NULL;
32850    // if parent.path is relative, search for it in PATH
32851    algo_lib::ResolveExecFname(parent.path);
32852    ret = execv(Zeroterm(parent.path),argv);
32853    return ret;
32854}
32855
32856// --- command.x2txn_proc.x2txn.ToCmdline
32857algo::tempstr command::x2txn_ToCmdline(command::x2txn_proc& parent) {
32858    algo::tempstr retval;
32859    retval << parent.path << " ";
32860    command::x2txn_PrintArgv(parent.cmd,retval);
32861    if (ch_N(parent.fstdin)) {
32862        retval << " " << parent.fstdin;
32863    }
32864    if (ch_N(parent.fstdout)) {
32865        retval << " " << parent.fstdout;
32866    }
32867    if (ch_N(parent.fstderr)) {
32868        retval << " 2" << parent.fstderr;
32869    }
32870    return retval;
32871}
32872
32873// --- command.x2txn_proc.x2txn.ToArgv
32874// Form array from the command line
32875void command::x2txn_ToArgv(command::x2txn_proc& parent, algo::StringAry& args) {
32876    ary_RemoveAll(args);
32877    ary_Alloc(args) << parent.path;
32878
32879    if (parent.cmd.in != "data") {
32880        cstring *arg = &ary_Alloc(args);
32881        *arg << "-in:";
32882        cstring_Print(parent.cmd.in, *arg);
32883    }
32884
32885    if (parent.cmd.prefix != "") {
32886        cstring *arg = &ary_Alloc(args);
32887        *arg << "-prefix:";
32888        cstring_Print(parent.cmd.prefix, *arg);
32889    }
32890    for (int i=1; i < algo_lib::_db.cmdline.verbose; ++i) {
32891        ary_Alloc(args) << "-verbose";
32892    }
32893}
32894
32895// --- command.x2txn_proc..Uninit
32896void command::x2txn_proc_Uninit(command::x2txn_proc& parent) {
32897    command::x2txn_proc &row = parent; (void)row;
32898
32899    // command.x2txn_proc.x2txn.Uninit (Exec)  //
32900    x2txn_Kill(parent); // kill child, ensure forward progress
32901}
32902
32903// --- command.x2write..ReadFieldMaybe
32904bool command::x2write_ReadFieldMaybe(command::x2write& parent, algo::strptr field, algo::strptr strval) {
32905    bool retval = true;
32906    command::FieldId field_id;
32907    (void)value_SetStrptrMaybe(field_id,field);
32908    switch(field_id) {
32909        case command_FieldId_in: {
32910            retval = algo::cstring_ReadStrptrMaybe(parent.in, strval);
32911            break;
32912        }
32913        case command_FieldId_gw: {
32914            retval = ietf::Ipport_ReadStrptrMaybe(parent.gw, strval);
32915            break;
32916        }
32917        default: break;
32918    }
32919    if (!retval) {
32920        algo_lib::AppendErrtext("attr",field);
32921    }
32922    return retval;
32923}
32924
32925// --- command.x2write..ReadTupleMaybe
32926// Read fields of command::x2write from attributes of ascii tuple TUPLE
32927bool command::x2write_ReadTupleMaybe(command::x2write &parent, algo::Tuple &tuple) {
32928    bool retval = true;
32929    ind_beg(algo::Tuple_attrs_curs,attr,tuple) {
32930        retval = x2write_ReadFieldMaybe(parent, attr.name, attr.value);
32931        if (!retval) {
32932            break;
32933        }
32934    }ind_end;
32935    return retval;
32936}
32937
32938// --- command.x2write..ToCmdline
32939// Convenience function that returns a full command line
32940// Assume command is in a directory called bin
32941tempstr command::x2write_ToCmdline(command::x2write& row) {
32942    tempstr ret;
32943    ret << "bin/x2write ";
32944    x2write_PrintArgv(row, ret);
32945    // inherit less intense verbose, debug options
32946    for (int i = 1; i < algo_lib::_db.cmdline.verbose; i++) {
32947        ret << " -verbose";
32948    }
32949    for (int i = 1; i < algo_lib::_db.cmdline.debug; i++) {
32950        ret << " -debug";
32951    }
32952    return ret;
32953}
32954
32955// --- command.x2write..PrintArgv
32956// print string representation of ROW to string STR
32957// cfmt:command.x2write.Argv  printfmt:Tuple
32958void command::x2write_PrintArgv(command::x2write& row, algo::cstring& str) {
32959    algo::tempstr temp;
32960    (void)temp;
32961    (void)str;
32962    if (!(row.in == "data")) {
32963        ch_RemoveAll(temp);
32964        cstring_Print(row.in, temp);
32965        str << " -in:";
32966        strptr_PrintBash(temp,str);
32967    }
32968    if (!(Ipport_Eq(row.gw, ietf::Ipport()))) {
32969        ch_RemoveAll(temp);
32970        Ipport_Print(row.gw, temp);
32971        str << " -gw:";
32972        strptr_PrintBash(temp,str);
32973    }
32974}
32975
32976// --- command.x2write..NArgs
32977// Used with command lines
32978// Return # of command-line arguments that must follow this argument
32979// If FIELD is invalid, return -1
32980i32 command::x2write_NArgs(command::FieldId field, algo::strptr& out_dflt, bool* out_anon) {
32981    i32 retval = 1;
32982    switch (field) {
32983        case command_FieldId_in: { // $comment
32984            *out_anon = false;
32985        } break;
32986        case command_FieldId_gw: { // $comment
32987            *out_anon = false;
32988        } break;
32989        default:
32990        retval=-1; // unrecognized
32991    }
32992    (void)out_dflt;//only to avoid -Wunused-parameter
32993    return retval;
32994}
32995
32996// --- command.x2write_proc.x2write.Start
32997// Start subprocess
32998// If subprocess already running, do nothing. Otherwise, start it
32999int command::x2write_Start(command::x2write_proc& parent) {
33000    int retval = 0;
33001    if (parent.pid == 0) {
33002        verblog(x2write_ToCmdline(parent)); // maybe print command
33003#ifdef WIN32
33004        algo_lib::ResolveExecFname(parent.path);
33005        tempstr cmdline(x2write_ToCmdline(parent));
33006        parent.pid = dospawn(Zeroterm(parent.path),Zeroterm(cmdline),parent.timeout,parent.fstdin,parent.fstdout,parent.fstderr);
33007#else
33008        parent.pid = fork();
33009        if (parent.pid == 0) { // child
33010            algo_lib::DieWithParent();
33011            if (parent.timeout > 0) {
33012                alarm(parent.timeout);
33013            }
33014            if (retval==0) retval=algo_lib::ApplyRedirect(parent.fstdin , 0);
33015            if (retval==0) retval=algo_lib::ApplyRedirect(parent.fstdout, 1);
33016            if (retval==0) retval=algo_lib::ApplyRedirect(parent.fstderr, 2);
33017            if (retval==0) retval= x2write_Execv(parent);
33018            if (retval != 0) { // if start fails, print error
33019                int err=errno;
33020                prerr("command.x2write_execv"
33021                <<Keyval("errno",err)
33022                <<Keyval("errstr",strerror(err))
33023                <<Keyval("comment","Execv failed"));
33024            }
33025            _exit(127); // if failed to start, exit anyway
33026        } else if (parent.pid == -1) {
33027            retval = errno; // failed to fork
33028        }
33029#endif
33030    }
33031    parent.status = parent.pid > 0 ? 0 : -1; // if didn't start, set error status
33032    return retval;
33033}
33034
33035// --- command.x2write_proc.x2write.StartRead
33036// Start subprocess & Read output
33037algo::Fildes command::x2write_StartRead(command::x2write_proc& parent, algo_lib::FFildes &read) {
33038    int pipefd[2];
33039    int rc=pipe(pipefd);
33040    (void)rc;
33041    read.fd.value = pipefd[0];
33042    parent.fstdout  << ">&" << pipefd[1];
33043    x2write_Start(parent);
33044    (void)close(pipefd[1]);
33045    return read.fd;
33046}
33047
33048// --- command.x2write_proc.x2write.Kill
33049// Kill subprocess and wait
33050void command::x2write_Kill(command::x2write_proc& parent) {
33051    if (parent.pid != 0) {
33052        kill(parent.pid,9);
33053        x2write_Wait(parent);
33054    }
33055}
33056
33057// --- command.x2write_proc.x2write.Wait
33058// Wait for subprocess to return
33059void command::x2write_Wait(command::x2write_proc& parent) {
33060    if (parent.pid > 0) {
33061        int wait_flags = 0;
33062        int wait_status = 0;
33063        int rc = -1;
33064        do {
33065            // really wait for subprocess to exit
33066            rc = waitpid(parent.pid,&wait_status,wait_flags);
33067        } while (rc==-1 && errno==EINTR);
33068        if (rc == parent.pid) {
33069            parent.status = wait_status;
33070            parent.pid = 0;
33071        }
33072    }
33073}
33074
33075// --- command.x2write_proc.x2write.Exec
33076// Start + Wait
33077// Execute subprocess and return exit code
33078int command::x2write_Exec(command::x2write_proc& parent) {
33079    x2write_Start(parent);
33080    x2write_Wait(parent);
33081    return parent.status;
33082}
33083
33084// --- command.x2write_proc.x2write.ExecX
33085// Start + Wait, throw exception on error
33086// Execute subprocess; throw human-readable exception on error
33087void command::x2write_ExecX(command::x2write_proc& parent) {
33088    int rc = x2write_Exec(parent);
33089    vrfy(rc==0, tempstr() << "algo_lib.exec" << Keyval("cmd",x2write_ToCmdline(parent))
33090    << Keyval("comment",algo::DescribeWaitStatus(parent.status)));
33091}
33092
33093// --- command.x2write_proc.x2write.Execv
33094// Call execv()
33095// Call execv with specified parameters
33096int command::x2write_Execv(command::x2write_proc& parent) {
33097    int ret = 0;
33098    algo::StringAry args;
33099    x2write_ToArgv(parent, args);
33100    char **argv = (char**)alloca((ary_N(args)+1)*sizeof(*argv));
33101    ind_beg(algo::StringAry_ary_curs,arg,args) {
33102        argv[ind_curs(arg).index] = Zeroterm(arg);
33103    }ind_end;
33104    argv[ary_N(args)] = NULL;
33105    // if parent.path is relative, search for it in PATH
33106    algo_lib::ResolveExecFname(parent.path);
33107    ret = execv(Zeroterm(parent.path),argv);
33108    return ret;
33109}
33110
33111// --- command.x2write_proc.x2write.ToCmdline
33112algo::tempstr command::x2write_ToCmdline(command::x2write_proc& parent) {
33113    algo::tempstr retval;
33114    retval << parent.path << " ";
33115    command::x2write_PrintArgv(parent.cmd,retval);
33116    if (ch_N(parent.fstdin)) {
33117        retval << " " << parent.fstdin;
33118    }
33119    if (ch_N(parent.fstdout)) {
33120        retval << " " << parent.fstdout;
33121    }
33122    if (ch_N(parent.fstderr)) {
33123        retval << " 2" << parent.fstderr;
33124    }
33125    return retval;
33126}
33127
33128// --- command.x2write_proc.x2write.ToArgv
33129// Form array from the command line
33130void command::x2write_ToArgv(command::x2write_proc& parent, algo::StringAry& args) {
33131    ary_RemoveAll(args);
33132    ary_Alloc(args) << parent.path;
33133
33134    if (parent.cmd.in != "data") {
33135        cstring *arg = &ary_Alloc(args);
33136        *arg << "-in:";
33137        cstring_Print(parent.cmd.in, *arg);
33138    }
33139
33140    if (true) {
33141        cstring *arg = &ary_Alloc(args);
33142        *arg << "-gw:";
33143        Ipport_Print(parent.cmd.gw, *arg);
33144    }
33145    for (int i=1; i < algo_lib::_db.cmdline.verbose; ++i) {
33146        ary_Alloc(args) << "-verbose";
33147    }
33148}
33149
33150// --- command.x2write_proc..Uninit
33151void command::x2write_proc_Uninit(command::x2write_proc& parent) {
33152    command::x2write_proc &row = parent; (void)row;
33153
33154    // command.x2write_proc.x2write.Uninit (Exec)  //
33155    x2write_Kill(parent); // kill child, ensure forward progress
33156}
33157
33158// --- command...SizeCheck
33159inline static void command::SizeCheck() {
33160}
33161
33162// --- command...StaticCheck
33163void command::StaticCheck() {
33164    algo_assert(_offset_of(command::FieldId, value) + sizeof(((command::FieldId*)0)->value) == sizeof(command::FieldId));
33165}